简体   繁体   中英

What does it mean to “Test to the Interface”?

I know this is kindof a generic programming question, but I have Googled it on several occasions in the past and I have never found a firm answer.

Several months back I had a conversation about Interfaces with a senior engineer at another company. He said he prefers to write Interfaces for everything because (among other things) it allows him to "test to the interface". I didn't think about the phrase too much at the time, (if I had I would have just asked him to explain!) but it confused me a bit.

I think this means he would write a unit test based on the interface, and that test would then be used to analyze every implementation of the interface. If thats what he meant, it makes sense to me. However, that explanation still left me wondering what the best practice would be when, for example, one of your implementations exposes additional public methods that are not defined in the interface? Would you just write an additional test for that class?

Thanks in advance for any thoughts on the subject.

Are you sure he said test to the interface and not program to the interface ?

In very simple terms what program to an interface means is that your classes should not depend on a concrete implementation. They should instead depend on an interface.

The advantage of this is that you can provide different implementations to an interface, and that enables you to unit test your class because you can provide a mock/stub to that interface.

Imagine this example:

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");
        }       
    }
}

Contrast that with this one:

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");
        }
    }
}

This enables you to write tests like this:

[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"));
}

Because in the second example you depend on interfaces and not concrete classes, you can easily swap them by fakes in your tests. And that is only one of the advantages, in the end it boils down to that this approach enables you to take advantage of polymorphism and that is useful not only for tests, but for any situation where you may want to provide alternative implementations for the dependencies of your class.

Full explanation of the example above can be found here .

Testing to an interface - while I've never heard that terminology before - would basically mean that while you test a concrete implementation of your interface, you only test the methods provided BY that interface. For example, consider the following classes:

interface A
{
    int MustReturn3();
}

class B : A
{
   public int MustReturn3()
   {
      return Get3();
   }
   public int Get3()
   {
      return 2 + 1;
   }
}

When you want to test an implementation of A , what do you test? Well, my implementation is B . I want to make sure that B accomplishes the tasks of A as it is supposed to.

I don't really care about testing Get3() . I only care that MustReturn3() will follow the interface detail, ie, it will return 3.

So I would write a test like so:

private A _a;

[TestInitialize]
public void Initialize()
{
    _a = new B();
}

[TestMethod]
public void ShouldReturn3WhenICallMustReturn3()
{
    Assert.AreEqual(3, _a.MustReturn3());
}

This ensures I am not testing any implementation detail ; I'm only testing what the interface tells me that the class implementation should do.

This is how I write my unit tests, actually. You can see a real working version of a test like this here .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM