单元测试-使单元测试调用其他单元测试是一种不好的形式

问题描述 投票:25回答:8

我有一个称为TestMakeAValidCall()的单元测试。它会测试我的电话应用程序能否发出有效呼叫。

我将要编写另一个名为TestShowCallMessage()的测试,需要对该测试进行有效的调用。在该测试中仅调用TestMakeAValidCall()是否是错误的形式?

供参考,这是我的TestMakeAValidCall()测试。

    [TestMethod]
    public void TestMakeAValidCall()
    {
       //Arrange
        phone.InCall = false;
        phone.CurrentNumber = "";
        // Stub the call to the database
        data.Expect(x => x.GetWhiteListData()).
            Return(FillTestObjects.GetSingleEntryWhiteList());
        // Get some bogus data
        string phoneNumber = FillTestObjects.GetSingleEntryWhiteList().
            First().PhoneNumber;
        // Stub th call to MakeCall() so that it looks as if a call was made.
        phone.Expect(x => x.MakeCall(phoneNumber)).
            WhenCalled(invocation =>
                       {
                           phone.CurrentNumber = phoneNumber;
                           phone.InCall = true;
                       });

       //Act
        // Select the phone number
        deviceControlForm.SelectedNumber = phoneNumber;
        // Press the call button to make a call.
        deviceMediator.CallButtonPressed();

       //Assert
        Assert.IsTrue(phone.InCall);
        Assert.IsTrue(phone.CurrentNumber == phoneNumber);
    }
c# visual-studio unit-testing
8个回答
53
投票

将设置重构为另一个方法,并从两个测试中调用该方法。测试不应调用其他测试。


11
投票

恕我直言,您应该执行以下操作之一:

  • 创建一个返回有效调用的方法,并将其分别用于两个测试(不能一个调用另一个)
  • 模拟ShowCallMessageTest的有效呼叫

6
投票

我认为这是个坏主意。您希望单元测试仅测试一件事和一件事。代替通过其他测试来创建呼叫,可以模拟呼叫并将其作为参数传递。


6
投票

提供一个对立点:

我坚信精心设计的单元测试应该相互依赖!

当然,只有在测试框架意识到这些依赖关系,以便当依赖关系失败时它可以停止运行依赖测试时,这才有意义。更好的是,这样的框架可以使夹具在测试之间通过,从而可以在不断增长和扩展的夹具上构建,而不必为每个测试从头开始重建它。当然,在同一示例中进行多个测试时,要进行缓存以确保不会产生副作用。

我们在JExample extension for JUnit中实现了这个想法。尽管有RubySmalltalk以及... most recent release of PHPUnit picked up both our ideas: dependencies and fixture reuse的端口,但尚无C#端口。

PS:folks are also using it for Groovy


4
投票

单元测试应按定义测试代码的一个单元/功能。让它调用其他单元测试将使其测试多个单元。我将其分解为单个测试。


2
投票

是-单元测试应该分开进行,并且应该只测试一件事(或至少测试少量紧密相关的东西)。顺便说一句,在测试方法中对data.Expect和phone.Expect的调用正在创建期望而不是存根调用,这会使您的测试变脆,如果您重构...


1
投票

单元与模块。...我们还认为测试也应该依赖于可重用的方法,并且应该在api级进行测试,以测试类的集成。许多只测试单个类,但是许多错误是在类级别之间进行的。我们还使用verifydesign来确保api不依赖于实现。这使您无需进行测试即可重构整个组件/模块(我们实际上进行了一次测试,效果很好)。当然,任何架构更改都会迫使您重构测试,但至少模块中的设计更改不会导致测试重构工作(除非您更改api的行为,当然当然就像触发更多的事件一样,但是“将会是“无论如何都是api更改)。


0
投票

“有人可以在这种情况下重新构造的方式吗?– PhilipBergström,2015年11月28日,15:33”

我目前正在做这样的事情,这就是我想出的:

注意,ProcessorType和BuildProcessor都调用TestLevels

除此之外的实际内容并不重要

使用XUnit和Shouldly NuGet包

private static void TestLevels(ArgProcessor incomingProcessor)
{
    Action<ProcessorLevel, int> currentLevelIteration = null;
    currentLevelIteration = (currentProcessor, currentLevel) =>
    {
        currentProcessor.CurrentLevel.ShouldBeEquivalentTo(currentLevel);
        ProcessorLevel nextProcessor = currentProcessor.CurrentProcessor;
        if (nextProcessor != null)
            currentLevelIteration(nextProcessor, currentLevel + 1);
    };

    currentLevelIteration(incomingProcessor, 0);
}

[Theory]
[InlineData(typeof(Build), "Build")]
public void ProcessorType(Type ProcessorType, params string[] args)
{
    ArgProcessor newCLI = new OriWeb_CLI.ArgProcessor(args);

    IncomingArgumentsTests.TestLevels(newCLI);

    newCLI.CurrentProcessor.ShouldBeOfType(ProcessorType);
}

[Theory]
[InlineData(typeof(Build.TypeScript), "TypeScript")]
[InlineData(typeof(Build.CSharp), "CSharp")]
public void BuildProcessors(Type ProcessorType, params string[] args)
{
    List<string> newArgs = new List<string> {"Build"};
    foreach(string arg in args) newArgs.Add(arg);

    ArgProcessor newCLI = new OriWeb_CLI.ArgProcessor(newArgs.ToArray());

    IncomingArgumentsTests.TestLevels(newCLI);

    newCLI.CurrentProcessor.CurrentProcessor.ShouldBeOfType(ProcessorType);
}
© www.soinside.com 2019 - 2024. All rights reserved.