我知道这是一个通用的编程问题,但我过去曾多次用Google搜索过,我从来没有找到一个坚定的答案。
几个月前,我与另一家公司的高级工程师讨论过Interfaces。他说他更喜欢为所有东西编写接口,因为(除其他外)它允许他“测试界面”。我当时并没有想太多这句话,(如果我有,我会请他解释一下!)但是我有点困惑。
我认为这意味着他将根据接口编写单元测试,然后该测试将用于分析接口的每个实现。如果这就是他的意思,那对我来说是有道理的。但是,这个解释仍然让我想知道什么是最佳实践,例如,您的某个实现暴露了未在接口中定义的其他公共方法?你会为那个班写一个额外的测试吗?
提前感谢您对该主题的任何想法。
答案 0 :(得分:3)
你确定他说测试界面而不是program to the interface吗?
简单来说,程序接口意味着你的类不应该依赖于具体的实现。他们应该依赖于接口。
这样做的好处是,您可以为接口提供不同的实现,并且可以对您的类进行单元测试,因为您可以为该接口提供模拟/存根。
想象一下这个例子:
public class SomeClass{
StringAnalyzer stringAnalizer = new StringAnalizer();
Logger logger = new Logger();
public void SomeMethod(){
if (stringAnalyzer.IsValid(someParameter))
{
//do something with someParameter
}else
{
logger.Log("Invalid string");
}
}
}
与此对比:
class SomeClass
{
IStringAnalyzer stringAnalizer;
ILogger logger;
public SomeClass(IStringAnalyzer stringAnalyzer, ILogger logger)
{
this.logger = logger;
this.stringAnalyzer = stringAnalyzer;
}
public void SomeMethod(string someParameter)
{
if (stringAnalyzer.IsValid(someParameter))
{
//do something with someParameter
}else
{
logger.Log("Invalid string");
}
}
}
这使您可以编写如下测试:
[Test]
public void SomeMethod_InvalidParameter_CallsLogger
{
Rhino.Mocks.MockRepository mockRepository = new Rhino.Mocks.MockRepository();
IStringAnalyzer s = mockRepository.Stub<IStringRepository>();
s.Stub(s => s.IsValid("something, doesnt matter").IgnoreParameters().Return(false);
ILogger l = mockRepository.DynamicMock<ILogger>();
SomeClass someClass = new SomeClass(s, l);
mockRepository.ReplayAll();
someClass.SomeMethod("What you put here doesnt really matter because the stub will always return false");
l.AssertWasCalled(l => l.Log("Invalid string"));
}
因为在第二个示例中,您依赖于接口而不是具体类,所以您可以在测试中通过 fakes 轻松交换它们。这只是其中一个优点,最终归结为这种方法可以让您利用多态,这不仅对测试有用,而且对于您可能需要的任何情况为您的类的依赖项提供替代实现。
可以找到上述示例的完整说明here。
答案 1 :(得分:2)
测试界面 - 虽然我以前从未听过这个术语 - 基本上意味着当你测试界面的具体实现时,你只测试那个界面提供的方法。例如,请考虑以下类:
interface A
{
int MustReturn3();
}
class B : A
{
public int MustReturn3()
{
return Get3();
}
public int Get3()
{
return 2 + 1;
}
}
如果您想测试A
的实施,您会测试什么?
好吧,我的实现是B
。我想确保B
按照预期完成A
的任务。
我真的不关心测试Get3()
。我只关心MustReturn3()
将遵循界面细节,即它将返回3.
所以我会写一个这样的测试:
private A _a;
[TestInitialize]
public void Initialize()
{
_a = new B();
}
[TestMethod]
public void ShouldReturn3WhenICallMustReturn3()
{
Assert.AreEqual(3, _a.MustReturn3());
}
这可确保我不测试任何实施细节;我只测试界面告诉我类实现应该做什么。
这就是我实际编写单元测试的方法。 您可以看到像here这样的测试的实际工作版本。
答案 2 :(得分:1)
它使单元测试更容易,因为您可以轻松地模拟接口以返回测试代码所需的数据。