71

这个问题与使用 MSTest 在 Visual Studio 中进行单元测试有关(这很重要,因为 MSTest 的执行顺序)。标记为 [TestInitialize] 的方法和测试类构造函数都将在每个测试方法之前运行。

所以,问题是,你倾向于在这些领域做什么?您是否避免在其中任何一个中进行某些活动?你的理由是什么:风格、技术、迷信?

4

10 回答 10

60

构造函数只是语言提供的一种结构。每个测试框架似乎都有自己的受控生命周期“初始化”。您可能只会在使用构造函数来改变您的本地人时遇到麻烦。

MSTest:您会为每个TestMethod. 这可能是唯一可以在构造函数、初始化程序或测试方法中改变本地变量而不影响其他测试方法的情况。

public class TestsForWhatever
{
    public TestsForWhatever()
    {
        // You get one of these per test method, yay!
    }

    [TestInitialize] 
    public void Initialize() 
    {
        // and one of these too! 
    }

    [TestMethod]
    public void AssertItDoesSomething() { }

    [TestMethod]
    public void AssertItDoesSomethingElse() { }
}

MSpec:你只会得到一个你所有EstablishBecause断言(It)。所以,不要在你的断言中改变你的本地人。并且不要依赖于基础上下文中本地人的突变(如果你使用它们)。

[Subject(typeof(Whatever))]
public class When_doing_whatever
{
    Establish context = () => 
    { 
        // one of these for all your Its
    };

    Because of = () => _subject.DoWhatever();

    It should_do_something;
    It should_do_something_else;
}
于 2011-08-16T14:15:59.137 回答
22

以下是我在 TestInitialize 中发现的一些优点。

  • 一些环境变量(例如TestContext)在测试类被实例化之前是不可访问的。
  • 可以通过标记基本 TestInitialize 方法抽象来要求使用派生类实现。
  • 可以轻松地覆盖基本的 TestInitialize 方法,并确定是在派生的 impl 之前、之后还是根本不调用基本 impl。相反,如果您从基测试类派生测试类,则在无参数构造函数的情况下,无论您是否有意,都会调用基 ctor。
  • 它的明确定义使意图清晰,并补充了 TestCleanup 方法。您可能会争辩说您可以为每个构造函数创建一个析构函数,但不能保证 MS Test 会按您的预期处理析构函数。
于 2011-12-31T17:07:36.003 回答
16

使用 TestInitialize() 或 ClassInitialize() 而不是测试类实例或静态构造函数的主要优点是其显式性质。它清楚地表明您正在测试之前进行一些设置。从长远来看,始终如一地这样做应该会提高可维护性。

于 2011-02-11T14:22:20.083 回答
4

我更喜欢使用该[TestInitialize]方法来执行被测试对象及其参数的实例化。如果需要实例化测试基类(通常是我创建或刷新存储库等的地方),我只在构造函数中执行工作。这有助于我将测试框架代码和测试代码在逻辑上和物理上分开。

于 2009-01-12T21:06:47.050 回答
3

这个问题也被问到(稍后)在 VS 测试框架中使用构造函数与 TestInitialize() 属性有什么区别?

FWIW 我假设“类构造函数”是指实例构造函数(而不是静态构造函数)。

我相信关于静态构造函数与ClassInitialize的问题同样可以问到您提出的相同问题...

于 2010-06-25T19:23:42.633 回答
3

我说除非你需要,否则使用构造函数TestContext

  1. 如果你能保持简单,为什么不呢。构造函数比魔法属性更简单。
  2. 您可以readonly在测试初始化​​中使用 which 是一件大事,您想为测试准备不应该更改的东西(理想情况下,您准备的东西也是不可变的)。
于 2017-07-23T21:46:05.250 回答
2

您测试的对象不需要在 [TestInitialize] 方法中实例化。您可以在测试方法 [Test] 中测试对象的构造函数。

[TestInitialize] 中的对象可以是设置您的持久性存储或准备测试对象将在测试中使用的值。

于 2008-12-02T16:18:16.397 回答
2

还有async另一个原因(在提出这个问题时不存在)[TestInitialize]。它允许您进行async设置操作(例如加载文件),这在构造函数中是不可能的:

        private string approver;        

        [TestInitialize]
        public async Task Initialize()
        {
            approver = await File.ReadAllTextAsync("approver.json");
        }
于 2019-06-13T09:21:12.483 回答
0

这取决于场景。如果您有一个测试类,并且出于某种奇怪的原因需要在另一个测试类上创建它的实例,则需要使用构造函数。

否则测试初始化​​更适合这个概念。首先,与上面写的原因相同,第二个 MS 可以在该属性上引入更多功能,您将受益于它们,使用构造函数您将坚持下去。

于 2017-03-10T15:38:42.467 回答
0

我希望有人仍然需要它。这是我的解决方案,如何对类构造函数进行单元测试。如果debuggingService 为空,我正在对类服务进行单元测试并抛出异常。

DebuggingStepTests 类构造函数

private readonly IDebuggingService debuggingService;

public string StepName { get; set; }

public DebuggingStep(IDebuggingService _debuggingService)
{
    _log.Starting();
    StepName = "DebuggingStep";

    debuggingService = _debuggingService 
        ?? throw new ArgumentException("DebuggingStep init failure due to => IDebuggingService null");
}

单元测试看起来像这样

    [Fact]
public void TestDebuggingStepConstructorWhen_InitServiceIsNull_ResultArgumentException() 
{
    //Arrange
    var arrange = new Action(() => 
    {
        new DebuggingStep(null);
    });

    //Act

    //Arrange
    Assert.Throws<ArgumentException>(arrange);
}

和实际结果: 在此处输入图像描述

希望这对某人有帮助

于 2019-12-27T16:15:45.993 回答