我偶尔会听到或读到人们在单元测试中断言他们的接口。我并不是要嘲笑一个用于其他类型测试的接口,而是专门创建一个伴随接口的测试。
考虑这个超级蹩脚和袖手旁观的例子:
public interface IDoSomething
{
string DoSomething();
}
和测试:
[TestFixture]
public class IDoSomethingTests
{
[Test]
public void DoSomething_Should_Return_Value()
{
var mock = new Mock<IDoSomething>();
var actualValue = mock.Expect(m => m.DoSomething()).Returns("value");
mock.Object.DoSomething();
mock.Verify(m => DoSomething());
Assert.AreEqual("value", actualValue);
}
}
我认为我的想法是使用测试来推动界面设计,并为实现者提供预期的指导,以便他们能够自己进行良好的测试。
这是一种常见的(推荐)做法吗?
答案 0 :(得分:24)
在我看来,仅使用模拟框架测试界面除了模拟框架本身之外几乎没有其他测试。我个人没有时间花在上面。
我想说应该驱动界面设计的是什么功能。我认为很难确定只使用模拟框架。通过创建界面的具体实现,需要与否的内容将变得更加明显。
我倾向于这样做的方式(我决不是推荐的方式,只是我的方式),就是在具体类型上编写单元测试,并引入接口需要用于依赖注入目的。
例如,如果测试中的具体类型需要访问某个数据层,我将为该数据层创建一个接口,为该接口创建一个模拟实现(或使用一个模拟框架),注入模拟实现并运行测试。在这种情况下,接口没有为数据层提供抽象的目的。
答案 1 :(得分:13)
我从来没有见过这样的东西,但似乎毫无意义。您可能希望测试这些接口的实现,而不是接口本身。
答案 2 :(得分:3)
接口是关于设计良好的合同,而不是良好实施的合同。由于C#不是一种允许接口在运行时未实现的动态语言,因此这种测试不适合该语言。如果它是Ruby或Perl,那么也许......
合同是一个想法。一个想法的健全性需要在设计时,而不是运行时间或测试时间对人进行审查。
实现可以是一组“功能”的空存根。那仍然会通过“接口”测试,但是合同执行不力。这仍然不意味着合同不好。
关于所有特定的界面测试完成是对原始意图的提醒,只需要您在意图改变时在2个位置更改代码。
答案 3 :(得分:2)
如果有可测试的黑盒级别要求,可以合理地预期您的接口的实施者可以通过,这是一种很好的做法。在这种情况下,您可以创建一个特定于该接口的测试类,用于测试该接口的实现。
public interface ArrayMangler
{
void SetArray (Array myArray);
Array GetSortedArray ();
Array GetReverseSortedArray();
}
您可以为ArrayMangler编写通用测试,并验证GetSortedArray返回的数组确实已经排序,并且GetReverseSortedArray确实是反向排序的。
在测试实现ArrayMangler的类时,可以包含测试,以验证是否满足合理预期的语义。
答案 4 :(得分:1)
在我看来,不是要走的路。创建接口作为重构(提取接口)而非TDD的行为。因此,您首先使用TDD创建一个类,然后提取一个接口(如果需要)。
答案 5 :(得分:0)
编译器本身执行接口的验证。 TDD执行接口的验证。
您可能希望查看c#4中的代码合同,因为您在如何使用该问题时略微接近该区域。你似乎把一些概念捆绑在一起,你可以理解的是混淆了。
你的问题的简短回答是你可能听错/误解了它。 TDD将推动接口的发展。
TDD通过验证覆盖是否实现而不涉及具体类型(实现接口的具体类型)来测试界面。
我希望这会有所帮助。
答案 6 :(得分:0)
接口是关于对象之间的关系,这意味着你不能在不知道被调用的上下文的情况下“测试 - 驱动”接口。我在调用接口的对象上使用TDD时使用接口发现,因为该对象需要来自其环境的服务。我不认为接口只能从类中提取,但这是另一个(也是更长的)讨论。
如果你不介意广告,我们的书http://www.growing-object-oriented-software.com/
还有更多内容