“测试界面”是什么意思?

时间:2014-01-17 16:09:35

标签: c# interface

我知道这是一个通用的编程问题,但我过去曾多次用Google搜索过,我从来没有找到一个坚定的答案。

几个月前,我与另一家公司的高级工程师讨论过Interfaces。他说他更喜欢为所有东西编写接口,因为(除其他外)它允许他“测试界面”。我当时并没有想太多这句话,(如果我有,我会请他解释一下!)但是我有点困惑。

认为这意味着他将根据接口编写单元测试,然后该测试将用于分析接口的每个实现。如果这就是他的意思,那对我来说是有道理的。但是,这个解释仍然让我想知道什么是最佳实践,例如,您的某个实现暴露了未在接口中定义的其他公共方法?你会为那个班写一个额外的测试吗?

提前感谢您对该主题的任何想法。

3 个答案:

答案 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)

它使单元测试更容易,因为您可以轻松地模拟接口以返回测试代码所需的数据。