visual-studio 单元测试 - 让单元测试调用其他单元测试是不好的形式吗
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1368900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unit Testing - Is it bad form to have unit test calling other unit tests
提问by Vaccano
I have a unit test called TestMakeAValidCall(). It tests my phone app making a valid call.
我有一个名为TestMakeAValidCall(). 它测试我的手机应用程序是否进行了有效呼叫。
I am about to write another test called TestShowCallMessage()that needs to have a valid call made for the test. Is it bad form to just call TestMakeAValidCall()in that test?
我即将编写另一个名为TestShowCallMessage()的测试,该测试需要为测试进行有效调用。只是调用TestMakeAValidCall()那个测试是不好的形式吗?
For reference this is my TestMakeAValidCall()test.
供参考,这是我的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);
}
回答by tvanfosson
Refactor the setup to another method and call that method from both tests. Tests should not call other tests.
将设置重构为另一个方法并从两个测试中调用该方法。测试不应调用其他测试。
回答by Samuel Carrijo
IMHO, you should do one of the following:
恕我直言,您应该执行以下操作之一:
- Create a method that returns a valid call, and use it separately for both tests (not one calling the other)
- Mock the valid call for the ShowCallMessageTest
- 创建一个返回有效调用的方法,并将其分别用于两个测试(不是一个调用另一个)
- 模拟 ShowCallMessageTest 的有效调用
回答by akuhn
To offer a counter point:
提供一个反点:
I strongly believe that well designed unit test shoulddepend on one another!
我坚信设计良好的单元测试应该相互依赖!
Of course, that makes sense only if the testing framework is aware of these dependencies such that it can stop running dependent test when a dependency fails. Even better, such a framework can pass the fixture from test to test, such that can build upon a growing and extending fixture instead of rebuilding it from scratch for each single test. Of course, caching is done to take care no side-effects are introduced when more than one test depends from the same example.
当然,这只有在测试框架知道这些依赖关系时才有意义,这样它才能在依赖关系失败时停止运行依赖测试。更好的是,这样的框架可以将夹具从一个测试传递到另一个测试,这样就可以构建在不断增长和扩展的夹具上,而不是为每个单独的测试从头开始重建它。当然,当多个测试依赖于同一个示例时,进行缓存是为了避免引入副作用。
We implemented this idea in the JExample extension for JUnit. There is no C# port yet, though there are ports for Rubyand Smalltalkand ... the most recent release of PHPUnit picked up both our ideas: dependencies and fixture reuse.
我们在JUnit的JExample 扩展中实现了这个想法。目前还没有 C# 端口,尽管有Ruby和Smalltalk 的端口,而且……最新版本的 PHPUnit 采纳了我们的两个想法:依赖项和夹具重用。
PS: folks are also using it for Groovy.
PS:人们也将它用于 Groovy。
回答by Rob Allen
I think its a bad idea. You want your unit test to test one thing and one thing only. Instead of creating a call through your other test, mock out a call and pass it in as an argument.
我认为这是一个坏主意。你希望你的单元测试只测试一件事和一件事。不要通过其他测试创建调用,而是模拟调用并将其作为参数传递。
回答by Vince
A unit test should test one unit/function of your code by definition. Having it call other unit tests makes it test more than one unit. I break it up in to individual tests.
根据定义,单元测试应该测试代码的一个单元/功能。让它调用其他单元测试使它测试多个单元。我把它分解成单独的测试。
回答by Lee
Yes - unit tests should be separate and should aim to test only one thing (or at least a small number of closely-related things). As an aside, the calls to data.Expect and phone.Expect in your test method are creating expectations rather than stub calls, which can make your tests brittle if you refactor...
是的 - 单元测试应该是分开的,并且应该只测试一件事情(或者至少是少数密切相关的事情)。顺便说一句,在您的测试方法中对 data.Expect 和 phone.Expect 的调用正在创建期望而不是存根调用,如果您重构,这会使您的测试变得脆弱......
回答by Dean Hiller
A unit vs. module....we also think tests should depend on reusable methods as well and should test at an api level testing integration of classes. Many just tests a single class but many bugs are at that integration between the class level. We also use verifydesign to guarantee the api does not depend on implementation. This allows you to refactor the whole component/module without touching a test(and we went through that once actually and it worked great). Of course, any architectural changes force you to refactor the tests but at least design changes in the module don't cause test refactor work(unless you change the behavior of the api of course implicitly like firing more events than you used to but that "would" be an api change anyways).
单元 vs. 模块……我们还认为测试也应该依赖于可重用的方法,并且应该在 api 级别测试类的集成。许多只测试一个类,但许多错误都在类级别之间的集成中。我们还使用 verifydesign 来保证 api 不依赖于实现。这允许您在不涉及测试的情况下重构整个组件/模块(我们实际上经历了一次并且效果很好)。当然,任何架构更改都迫使您重构测试,但至少模块中的设计更改不会导致测试重构工作(除非您当然隐式地更改 api 的行为,例如触发比以前更多的事件,但是“无论如何都将是一个api更改)。
回答by MatthewOrionos
"Could someone ellaborate on how the refactoring would look like in this case? – Philip Bergstr?m Nov 28 '15 at 15:33"
“有人可以详细说明在这种情况下重构的样子吗? – Philip Bergstr?m,2015 年 11 月 28 日,15:33”
I am currently doing something like this and this is what i came up with:
我目前正在做这样的事情,这就是我想出的:
Notice that ProcessorType and BuildProcessors both call TestLevels
注意 ProcessorType 和 BuildProcessors 都调用 TestLevels
the actual content besides that fact is unimportant
除了那个事实之外的实际内容并不重要
its using XUnit, and Shouldly NuGet package
它使用 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);
}

