我有一个称为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);
}
将设置重构为另一个方法,并从两个测试中调用该方法。测试不应调用其他测试。
恕我直言,您应该执行以下操作之一:
我认为这是个坏主意。您希望单元测试仅测试一件事和一件事。代替通过其他测试来创建呼叫,可以模拟呼叫并将其作为参数传递。
提供一个对立点:
我坚信精心设计的单元测试应该相互依赖!
当然,只有在测试框架意识到这些依赖关系,以便当依赖关系失败时它可以停止运行依赖测试时,这才有意义。更好的是,这样的框架可以使夹具在测试之间通过,从而可以在不断增长和扩展的夹具上构建,而不必为每个测试从头开始重建它。当然,在同一示例中进行多个测试时,要进行缓存以确保不会产生副作用。
我们在JExample extension for JUnit中实现了这个想法。尽管有Ruby和Smalltalk以及... most recent release of PHPUnit picked up both our ideas: dependencies and fixture reuse的端口,但尚无C#端口。
单元测试应按定义测试代码的一个单元/功能。让它调用其他单元测试将使其测试多个单元。我将其分解为单个测试。
是-单元测试应该分开进行,并且应该只测试一件事(或至少测试少量紧密相关的东西)。顺便说一句,在测试方法中对data.Expect和phone.Expect的调用正在创建期望而不是存根调用,这会使您的测试变脆,如果您重构...
单元与模块。...我们还认为测试也应该依赖于可重用的方法,并且应该在api级进行测试,以测试类的集成。许多只测试单个类,但是许多错误是在类级别之间进行的。我们还使用verifydesign来确保api不依赖于实现。这使您无需进行测试即可重构整个组件/模块(我们实际上进行了一次测试,效果很好)。当然,任何架构更改都会迫使您重构测试,但至少模块中的设计更改不会导致测试重构工作(除非您更改api的行为,当然当然就像触发更多的事件一样,但是“将会是“无论如何都是api更改)。
“有人可以在这种情况下重新构造的方式吗?– 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);
}