【问题标题】:VS Team Test: Multiple Test Initialize Methods in Test ClassVS Team Test:测试类中的多个测试初始化​​方法
【发布时间】:2012-05-28 19:29:22
【问题描述】:

我在 TeamTest 中有一个名为“MyClassTest”的单元测试项目。这个项目有三个TestMethods。每个方法都需要自己的测试初始化​​步骤。但是当我将 TestInitializeAttribute 应用于三个初始化方法时,它说该属性不应多次使用。那么在 Visual Studio Team Test 中用于初始化每个测试方法的属性应该是什么?

参考:

  1. VS Team Test: .Net Unit Testing with Excel as Data Source: Adapter Failed

  2. How to create Startup and Cleanup script for Visual Studio Test Project?

  3. VS 2010 Load Tests Results with custom counters

  4. How to log unit test entry and leave in MSTest

  5. Can a unit test project load the target application's app.config file?

【问题讨论】:

    标签: c# .net visual-studio-2010 unit-testing


    【解决方案1】:

    在我的工作中,我们将参数传递给 TestInitialize 方法以确定我们希望初始化如何工作。

    public partial class CommonActions
    {
       public void TestInitialize(bool adminTest)
       {
          try
          {
             if (adminTest)
             {
                //do stuff
             } 
    

    然后我们在类定义中有一个标准的初始化,默认为 false。

    [TestClass]
    public class ProjectTestBase : FrameworkTestBase
    { 
      public CommonActions common { get; set; } = new CommonActions();
    
      [TestInitialize]
       public void TestInitialize() => common.TestInitialize(false);
    

    然后在测试用例本身中,您可以为任何您想要的测试覆盖 TestInitialize。

    [TestClass]
    public class SetReportsInAdmin : ProjectTestBase
    {
        [TestInitialize]
        public new void TestInitialize() => common.TestInitialize(true);
    

    我们使用布尔值来判断 Admin 测试是否需要额外的设置开销。采取这个并应用您想要的任何变量,通过使用一种方法为您提供多次初始化。

    【讨论】:

      【解决方案2】:

      根据MSDNTestInitializeAttribute:

      • 不能多次使用(AllowMultiple = false),并且
      • 不能被继承来创建你自己的TestInitializeAttribute。

      所以,我的建议是创建没有TestInitialize 属性的测试初始化​​方法。然后在唯一的TestInitialize 方法中检查当前执行的TestMethod 并调用相应的初始化方法:

      [TestClass]
      public class UnitTest
      {
          public TestContext TestContext { get; set; }
      
          [TestInitialize]
          public void Initialize()
          {
              switch (TestContext.TestName)
              {
                  case "TestMethod1":
                      this.IntializeTestMethod1();
                      break;
                  case "TestMethod2":
                      this.IntializeTestMethod2();
                      break;
                  default:
                      break;
              }
          }
      
          [TestMethod]
          public void TestMethod1()
          {
          }
      
          [TestMethod]
          public void TestMethod2()
          {
          }
      
          public void IntializeTestMethod1()
          {
              //Initialize Test Method 1
          }
      
          public void IntializeTestMethod2()
          {
              //Initialize Test Method 2
          }
      }
      

      【讨论】:

      • 谢谢。这似乎可行。这是人们遵循的标准做法吗?
      • 这是我的团队所遵循的做法。不知道有没有更好的办法:)
      • AllowMultiple = false 仅在同一个元素中强制使用单一用法...这意味着不能用多个 TestInitializeAttribute 标记单个方法。不能用于不同方法的规则,与AllowMultiple = false无关。
      • 虽然这行得通,但它的缺点是,如果重构了测试名称并且没有更新魔术字符串,测试就会中断。为什么初始化不能成为测试“排列”部分的一部分(即,如果多个测试需要相同的初始化,则在测试主体或私有方法中)?然后可以从测试中的 try..finally 块启动清理。我个人是这样做的,到目前为止效果很好。
      【解决方案3】:

      这是一个有点旧的帖子,但我想出了以下似乎可以正常工作的内容: 首先,定义一个属性类:

      [AttributeUsage(AttributeTargets.Method, Inherited = true)]
      public class InitialiseWithAttribute : Attribute
      {
          public string Id { get; private set; }
      
          public InitialiseWithAttribute(string id)
          {
              Id = id;
          }
      }
      

      然后在一些方便的实用程序类中定义一个扩展方法:

          public static bool IsInitialisedWith(this string testName, string value)
          {
              bool result = false;
              Type testClassType = new StackFrame(1).GetMethod().DeclaringType;
              MethodInfo methodInfo = testClassType.GetMethod(testName);
              if (methodInfo != null)
              {
                  InitialiseWithAttribute initialiseWithAttribute =
                      methodInfo.GetCustomAttribute<InitialiseWithAttribute>(true);
                  if (initialiseWithAttribute != null)
                  {
                      result = initialiseWithAttribute.Id == value;
                  }
              }
              return result;
          }
      

      现在编写你的测试,因此:

          public TestContext TestContext {get; set;}
          [TestInitialize]
          public void TestInitialise()
          {
              if (TestContext.TestName.IsInitalisedWith("DoSomethingSpecial")
              {
                   // ... Do something special
              }
              else
              {
                   // ... Do something normal
              }
          }
      
          [TestMethod]
          [InitialiseWith("DoSomethingSpecial")]
          public void MySpecialTest()
          {
               // The test
          }
      

      【讨论】:

        【解决方案4】:

        如果您有三个测试方法,并且每个方法都有自己的初始化步骤,那么为什么要将初始化转移到将在每次测试之前运行的方法?我看到的唯一好处是那个不错的 switch 块,它在你的源文件中添加了一些行。但它给您带来了缺点 - 查看这些测试方法中的任何一种,您都无法真正判断将在哪个上下文方法中执行。所以,我使用初始化方法只设置基本上下文,这是夹具中所有测试真正使用的。

        只需将上下文创建移动到每个方法的arrange 部分。

        如果您有多个使用公共上下文的方法,那么只需提取方法,该方法将为它们设置上下文,并在arrange 部分调用它。您还可以将每个上下文设置拆分为多个步骤并重复使用这些步骤(就像在 Given-When-Then 工具中所做的那样,例如 Specflow)。

        当然,创建不同的灯具也是一种选择。

        【讨论】:

        • 在通过集成测试进行负载测试的范围内,例如在负载测试 WCF 服务层的情况下,您希望测试方法尽可能干净,因为测试方法执行时间是什么将被跟踪。这是一个明显的例子,将所有初始化逻辑移动到测试初始化​​是唯一的方法!
        • @DavidRodrigues 不仅如此。您可以为要测试的每个功能创建单独的 TestFixture 类。这将非常简单,而且它的 SetUp 方法不会有任何开关块。另外,我会使用 SpecFlow 之类的东西进行验收测试。有很好的 Given 方法,它们组织得很好并且可以重用。 SpecFlow 将所有步骤执行时间写入输出窗口
        【解决方案5】:

        如果他们需要三个单独的初始化;那么它们可能应该在三个独立的装置中,每个装置都有自己的 init!

        【讨论】:

        • Schaliasos 建议了上述方法。那应该没问题。你觉得有什么不足吗?
        • 不.. 不是真的。只要这三个测试在逻辑上是相关的。我个人..我会把它分成三个固定装置,但这是个人喜好。上面看起来不错。您还需要在之后进行测试清理吗?可以使用相同的方法吗?
        猜你喜欢
        • 1970-01-01
        • 2017-09-19
        • 2010-11-28
        • 1970-01-01
        • 2011-05-21
        • 2019-02-20
        • 1970-01-01
        • 2018-03-10
        • 1970-01-01
        相关资源
        最近更新 更多