2016-12-27 9 views
0

テストスイートの実行時間を短縮するために、自動機能テストを並行して改善する作業に取り組んでいます。私が抱えている問題は、テストが並行して実行されているときにテストデータを管理する方法を理解することです。テストが並行して実行されているときにテストデータを管理する方法

具体的には、テストが連続して実行されるときに、問題なくアプリケーションに1人のユーザーしかログインしません。ただし、そのユーザーとの複数のテストを実行してログインしようとすると、これは機能しません。私はログインに使用できる他のユーザーがいます。私の質問は、1人のユーザーが1人のユーザーを使用するように、これらのユーザーをどのように管理できますか?彼が自由になるまで他のテストでそのユーザーに触れることはありません。

疑似コードまたはコードサンプルを提供できる場合は、実際に役立ちます。事前に

感謝:)

@grafitoからのフィードバックの後、これは私が思いついたのソリューションです。完全に働いているようだ。すべてのユーザーがConnectedになると、利用できるようになるまでループしてしまいます。

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

あなたはユニットテストをタグ付けが、統合テストを記述しているように見えてきました。 2つの戦略は必ずしも同じではありません。 – 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

IMOここに銀の弾丸の解決策はありません - 各アプリケーションごとに異なる話です。私は

  • 別々のユーザーを助けることができる汎用ルールの唯一のカップルを提供することができます - 私の同僚は、すでに述べているように確かに、同じテストをすることによって

  • 別々のユーザーと同じユーザーで実行すべきではありませんアプリケーションによってサポートされる抽象化。たとえば、アプリケーションがマルチテナントをサポートしている場合は、異なるテナントで並列テストを実行することをお勧めします。そうでなければ、テスト実行中に変更されたメモリ内データ構造にアクセスするときにテストが衝突する可能性があります

  • 各テストで新規ユーザー、新規テナントなどを作成しても、テストが遅すぎるからです。

  • パーシスタンスレイヤーを分けてください。たとえば、アプリケーションが何らかの種類のデータベースで動作する場合、2つのテストを並行して実行すると、同じテーブル内の同じ行と列が同じように変更される可能性があります。

  • テストでは、アプリケーションの永続レイヤ/メモリ内データ構造を「ダーティ」状態のままにしないでください。これは、並行テストだけでなく、連続テストでも正しいです。残念ながらそのことは必ずしも可能ではありません。

  • 最後の項目が達成できない場合は、他の選択肢がない場合にのみプールを使用してください。あなたは、時間のある時点で、ユーザーのプールを作成した場合たとえば、& Bが同じユーザーで実行されますテストします。テストAが使用不能にユーザーに割り当てられたデータ構造を作った場合

  • が不要なテストを避け、予想通り(ダーティそれらを左)、その後、テストBはおそらく実行されません。開発者は単体テスト、統合テスト、機能テストを持っていますが、すべてソフトウェアをチェックするのに有効なツールです。だから、何かがユニットテストでカバーできるのであれば - 本格的な機能テストと一緒に行くそうでない場合、統合テストで行くそうでない場合は、それのために行きます。あなたが(常にシステムにログインなどのような)機能テストを記述しているようです。それは本当に必要ですか?オーバーヘッドではありませんか?たぶん、あなたはあなたのアプリケーション/のみ関連部品の一部だけを実行する統合テストでは、既存のコンポーネントの動作をチェックしてみてください...私はこのアドバイスがあまりにも一般的であることを知り、あなたの質問の内容だけでなく適用されますそれは暗黙のうちに希望

全体のアプリケーションのための正しいテストインフラストラクチャを設計する際に役立つことができ、これは@grafitoからのフィードバック後

0

を助け、しかし、これは私が思いついたのソリューションです。完全に働いているようだ。すべてのユーザーがConnectedになると、利用できるようになるまでループしてしまいます。

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; 
    } 
} 

}

関連する問題