2016-12-27 133 views
0

我正致力于改进我的自动化功能测试以并行运行以减少测试套件的运行时间。我遇到的问题是了解如何在测试并行运行时管理我的测试数据。如何在并行运行测试时管理测试数据

更具体地说,当测试连续运行时,我使用单个用户登录到应用程序没有问题。但是,当试图使用该用户运行多个测试并尝试登录时,这不起作用。我有其他用户可以用来登录。我的问题是如何管理这些用户,以便1次测试使用1个唯一用户?除非他有空,否则没有其他测试会触动该用户。

如果你可以提供一些伪代码或代码示例,它确实会有所帮助。

感谢提前:)

从@grafito反馈之后,这是我想出了一个解决方案。似乎正在全力工作。当所有用户都连接时,我们会陷入循环,直到有一个可用。

namespace UnitTestProject1 
{ 
[TestFixture] 
[Parallelizable] 
[Category("ParallelTest2")] 
public class ParallelTestingExampleV2_A : BaseTest 
{ 
    [Test] 
    public void TestMethod1() 
    { 
     Trace.WriteLine($"Test1 has user {AvailableUser.UserName}"); 
     Thread.Sleep(50000); 
     Trace.WriteLine($"Test1 sleeping for 50000ms so one user is Connected."); 
    } 
} 
[TestFixture] 
[Parallelizable] 
[Category("ParallelTest2")] 
public class ParallelTestingExampleV2_B : BaseTest 
{ 
    [Test] 
    public void TestMethod2() 
    { 
     Trace.WriteLine($"Test2 has user {AvailableUser.UserName}"); 
    } 
} 
[TestFixture] 
[Parallelizable] 
[Category("ParallelTest2")] 
public class ParallelTestingExampleV2_C : BaseTest 
{ 
    [Test] 
    public void TestMethod3() 
    { 
     Trace.WriteLine($"Test3 has user {AvailableUser.UserName}"); 
    } 
} 

[SetUpFixture] 
public class TestFixtureForTestsNamespace 
{ 
    public static ListOfUsers ListOfAllPossibleUsers; 

    [OneTimeSetUp] 
    public void RunBeforeAllTestsAreExecutedInSomeNamespace() 
    { 
     GetPoolOfUsers(); 
    } 

    private static void GetPoolOfUsers() 
    { 
     var oneUser = new User 
     { 
      UserName = "a", 
      Password = "a" 
     }; 

     var secondUser = new User 
     { 
      UserName = "b", 
      Password = "b" 
     }; 
     ListOfAllPossibleUsers = new ListOfUsers() { oneUser, secondUser }; 
    } 
} 

public class BaseTest 
{ 
    protected User AvailableUser; 

    [SetUp] 
    public void SetupForEveryTestMethod() 
    { 
     AvailableUser = TestFixtureForTestsNamespace.ListOfAllPossibleUsers.GetAvailableUser(); 
    } 

    [TearDown] 
    public void TearDownForEveryTestMethod() 
    { 
     TestFixtureForTestsNamespace.ListOfAllPossibleUsers.ReleaseUser(AvailableUser); 
    } 
} 



public class User 
{ 
    internal string UserName = ""; 
    internal string Password = ""; 
    internal bool Connected; 
} 

public class ListOfUsers : List<User> 
{ 
    internal void ReleaseUser(User userToBeReleased) 
    { 
     lock (this) { userToBeReleased.Connected = false; } 
    } 

    internal User GetAvailableUser() 
    { 
     User user = null; 
     while (user == null) 
     { 
      lock (this) 
      { 
       for (int i = 0; i < Count && user == null; i++) 
       { 
        if (!this[i].Connected) 
         user = this[i]; 
       } 
       if (user != null) 
        user.Connected = true; 
      } 
      Thread.Sleep(200); 
     } 
     return user; 
    } 
} 

}

+0

您所标记的单元测试,但似乎是描述集成测试。两者的策略不一定相同。 – Kritner

回答

1

创建用户池,并运行一个“新”的测试时,得到了用户的池的用户。如果所有用户已连接,请等待用户(终止测试结束时释放)。

internal class UserDef 
{ 
    internal string Login = "" ; // eventually add password 
    internal bool Connected = false ; 
} 

internal class UserDefs : List<UserDef> 
{ 
    internal ReleaseUser(UserDef userdef) 
    { 
    lock(this) { userdef.Connected=false ; } 
    } 

    internal UserDef GetAvailableUser() 
    { 
    UserDef result=null ; 
    while (result==null) 
    { 
     lock(this) 
     { 
     for (int i=0;i<Count && result==null;i++) if (!this[i].Connected) result=this[i] ; 
     if (result!=null) result.Connected = true ; 
     } 
     system.Threading.Thread.Sleep(200) ; 
    } 
    return result ; 
    } 
} 
+0

谢谢,我已经使用你的代码来完成我的实现,并将其添加到我的问题中。 –

0

使测试独立 - 为每个测试创建一个新用户!

用户的“池”具有复杂性,可能会给您带来问题。如果为每个测试创建一个新用户是一件痛苦的事情,那么您需要解决这个问题。但它将是值得的!

+0

我喜欢这个选项,我认为这将是一个理想的解决方案。然而,这是迄今为止最困难的解决方案。应用程序的体系结构不容易做到这一点。用户与数据紧密耦合。只有特定用户才能访问特定内容。我没有API可以轻松创建和删除测试用户。这需要开发团队付出很大的努力才能做到这一点。虽然这是我的梦想哈哈,但它在短期内不会帮助我 –

0

国际海事组织在这里没有银弹解决方案 - 每个应用程序都有不同的故事。我只能提供一对夫妇只的通用规则,可以帮助

  • 独立的用户 - 确实像我的同事已经说过,同样的测试不应该使用相同的用户

  • 独立的运行抽象支持你的应用程序。例如,如果您的应用程序支持多租户,请尽量在不同租户中运行并行测试。否则,在访问内存中的数据结构时,测试可能会发生冲突,在测试运行期间发生更改

  • 话虽如此,确保为每个测试创建新用户,新租户等不会产生大量开销,因为否则您的测试将会太慢

  • 分隔持久层。例如,如果您的应用程序使用某种数据库,则2个并行运行的测试可以再次更改同一个表中的相同行和列,它们会发生冲突。

  • 测试不应该使应用程序持久层/内存数据结构处于“脏”状态。即使连续测试,这也是正确的,不仅是平行的。不幸的是它并不总是可能的

  • 如果最后一项无法实现,只有在没有其他选择的情况下才使用池。例如,如果您创建了一个用户池,在某个时间点测试A & B将在同一用户下运行。如果测试A使分配给用户的数据结构不可用(使它们变脏),则测试B可能无法按预期运行

  • 避免不必要的测试。开发人员有单元测试,集成测试,功能测试 - 都是检查软件的有效工具。所以如果单元测试能够覆盖某些东西 - 去做它,否则就去集成测试,否则就去完成功能测试。你似乎描述了一个功能测试(例如总是登录到系统等等)。它真的需要吗?这不是一个开销吗?也许你应该尝试在集成测试中检查现有组件的行为,该测试将仅运行应用程序的一部分/仅运行相关组件......我知道这个建议太笼统了,不仅适用于你的问题的上下文,但它可以在设计正确的测试基础设施为整个应用程序隐含帮助

希望这有助于

0

从@grafito反馈之后,这是我想出了一个解决方案。似乎正在全力工作。当所有用户都连接时,我们会陷入循环,直到有一个可用。

namespace UnitTestProject1 
{ 
[TestFixture] 
[Parallelizable] 
[Category("ParallelTest2")] 
public class ParallelTestingExampleV2_A : BaseTest 
{ 
    [Test] 
    public void TestMethod1() 
    { 
     Trace.WriteLine($"Test1 has user {AvailableUser.UserName}"); 
     Thread.Sleep(50000); 
     Trace.WriteLine($"Test1 sleeping for 50000ms so one user is Connected."); 
    } 
} 
[TestFixture] 
[Parallelizable] 
[Category("ParallelTest2")] 
public class ParallelTestingExampleV2_B : BaseTest 
{ 
    [Test] 
    public void TestMethod2() 
    { 
     Trace.WriteLine($"Test2 has user {AvailableUser.UserName}"); 
    } 
} 
[TestFixture] 
[Parallelizable] 
[Category("ParallelTest2")] 
public class ParallelTestingExampleV2_C : BaseTest 
{ 
    [Test] 
    public void TestMethod3() 
    { 
     Trace.WriteLine($"Test3 has user {AvailableUser.UserName}"); 
    } 
} 

[SetUpFixture] 
public class TestFixtureForTestsNamespace 
{ 
    public static ListOfUsers ListOfAllPossibleUsers; 

    [OneTimeSetUp] 
    public void RunBeforeAllTestsAreExecutedInSomeNamespace() 
    { 
     GetPoolOfUsers(); 
    } 

    private static void GetPoolOfUsers() 
    { 
     var oneUser = new User 
     { 
      UserName = "a", 
      Password = "a" 
     }; 

     var secondUser = new User 
     { 
      UserName = "b", 
      Password = "b" 
     }; 
     ListOfAllPossibleUsers = new ListOfUsers() { oneUser, secondUser }; 
    } 
} 

public class BaseTest 
{ 
    protected User AvailableUser; 

    [SetUp] 
    public void SetupForEveryTestMethod() 
    { 
     AvailableUser = TestFixtureForTestsNamespace.ListOfAllPossibleUsers.GetAvailableUser(); 
    } 

    [TearDown] 
    public void TearDownForEveryTestMethod() 
    { 
     TestFixtureForTestsNamespace.ListOfAllPossibleUsers.ReleaseUser(AvailableUser); 
    } 
} 



public class User 
{ 
    internal string UserName = ""; 
    internal string Password = ""; 
    internal bool Connected; 
} 

public class ListOfUsers : List<User> 
{ 
    internal void ReleaseUser(User userToBeReleased) 
    { 
     lock (this) { userToBeReleased.Connected = false; } 
    } 

    internal User GetAvailableUser() 
    { 
     User user = null; 
     while (user == null) 
     { 
      lock (this) 
      { 
       for (int i = 0; i < Count && user == null; i++) 
       { 
        if (!this[i].Connected) 
         user = this[i]; 
       } 
       if (user != null) 
        user.Connected = true; 
      } 
      Thread.Sleep(200); 
     } 
     return user; 
    } 
} 

}