【问题标题】:MsTest ClassInitialize and InheritanceMsTest 类初始化和继承
【发布时间】:2019-11-17 00:44:55
【问题描述】:

我有一个用于测试的基类,其组成方式如下:

[TestClass]
public abstract class MyBaseTest
{
   protected static string myField = "";

   [ClassInitialize]
   public static void ClassInitialize(TestContext context)
   {
       // static field initialization
       myField = "new value";
   }
}

现在我正在尝试创建一个从基础继承的新测试,具有以下签名:

[TestClass]
public class MyTest : MyBaseTest
{
   [TestMethod]
   public void BaseMethod_ShouldHave_FieldInitialized()
   {
       Assert.IsTrue(myField == "new value");
   }
}

ClassInitialize 永远不会被子测试调用...在 MsTest 上使用继承的测试初始化​​真正正确的方法是什么?

【问题讨论】:

标签: c# mstest


【解决方案1】:

很遗憾,您无法以这种方式实现这一目标,因为 ClassInitializeAttribute Class 无法被继承。

继承的属性可以被使用它的类的子类使用。由于ClassInitializeAttribute不能被继承,所以当MyTest类初始化时,不能调用MyBaseTest类的ClassInitialize方法。

尝试用另一种方式解决它。一种效率较低的方法是在MyTest 中再次定义ClassInitialize 方法,然后只调用基本方法而不是复制代码。

【讨论】:

  • 我尝试了你的方法并且它有效,但老实说微软应该解决这个问题,因为 NUnit 没有这种行为。
  • 当你说“ClassInitializeAttribute 类不能被继承”时,你指的是 sealed 的类吗?——这不应该影响属性在应用于继承的方法时的显示方式……
  • 哦,我刚刚注意到方法是static,这意味着它们不参与正常的继承。啊……
  • 您可以在用户语音here中对此功能进行投票
【解决方案2】:

一个潜在的解决方法是使用AssemblyInitializeAttribute 定义一个新类。显然,它有不同的范围,但对我来说,它满足了我的需求(横切关注点,恰好需要对每个测试类和测试方法进行完全相同的设置。)

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MyTests
{
  [TestClass]
  public sealed class TestAssemblyInitialize
  {
    [AssemblyInitialize]
    public static void Initialize(TestContext context)
    {
      ...
    }
  }
}

【讨论】:

  • 这也是我最终做的,但是我很好奇;为什么你的基础测试类是sealed?这不会阻止您继承该类并完全按照您的计划去做吗?
  • 我没有用它来继承。我所有的真实测试类都派生自不同的基类。这个类纯粹用于AssemblyInitialize 方法。这只是我为了让事物分开而做出的设计决定。
【解决方案3】:

在基类上使用静态构造函数?按照设计,它只执行一次,并且没有像 ClassInitializeAttribute 这样的奇怪的继承限制。

【讨论】:

  • 这不是[ClassInitialize] 的替代品:加载完整程序集时,它将在没有上下文的情况下调用。所以还不如[AssemblyInitialize]
【解决方案4】:

更新:添加锁以避免多线程问题...

我们知道,当类运行时,会为类中的每个 [TestMethod] 构造一个新实例。每次发生这种情况时,都会调用基类的无参数构造函数。难道不能简单地在基类中创建一个静态变量,并在构造函数运行时对其进行测试吗?

这有助于您不要忘记将初始化代码放在子类中。

不确定这种方法是否有任何缺点......

像这样:

public class TestBase
{
    private static bool _isInitialized = false;
    private object _locker = new object();

    public TestBase()
    {
        lock (_locker) 
        {
          if (!_isInitialized)
          {
            TestClassInitialize();
            _isInitialized = true;
          }
        }
    }

    public void TestClassInitialize()
    {
        // Do one-time init stuff
    }
}
public class SalesOrderTotals_Test : TestBase
{
    [TestMethod]
    public void TotalsCalulateWhenThereIsNoSalesTax()
    {
    }
    [TestMethod]
    public void TotalsCalulateWhenThereIsSalesTax()
    {
    }
}

【讨论】:

  • 您没有使用此解决方案获得 TestContext。真可惜。
  • 另外,你没有清理
  • 而且您通常不需要其中任何一个。对于合适的情况,这是一个很好的解决方案,通常情况下。
  • 这里的一个严重缺陷是线程安全(或缺乏)。由于所有测试都将并行运行,并且它们都会在大约 10 分钟时调用构造函数。同时,它们中的大多数将进入 TestClassInitialize() 方法,直到其中一个最终退出该方法并将 _isInitialized 设置为 true。
  • 静态成员完全特定于声明类;子类不会获得单独的副本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多