问题描述:

As i'm learning more about unit testing i've come to realize the behavior (interaction) vs. state verification approaches that are well known in this field.

Verifying the state of some system after performing a certain operation seems logical to me.

Can the same be said about verifying the interaction of the class under test with some other component? i am still not 100% convinced.

For example:

public void DoSomething(IDependency dependency)

{

// some code ...

dependency.Method();

dependency.Method2();

// some more code ...

}

Does the fact that current implementation of DoSomething() call Method() and Method2() and is testable using mocks have any real value?

Isn't the calling of these 2 methods an implementation detail of the DoSomething() ?

It seems that verifying interactions is a lot more fragile when compared to state based verification, and also breaks encapsulation (testing something that is hidden by this method).

网友答案:

If a specification (some call it test) requires that a certain kind of interaction takes place, you should also verify that this is the case and have a unit test for this. For example if it is required that the SUT saves a file, you should probably verify that IFileSystem.Save(...) has been called. And if is required that the SUT should safe it with a new filename if the file exists you should verify that IFileSystem.Save(...) has been called with the right filename and that IFileSystem.Exists(...) has been called. This is interaction testing at its best.

Using FakeItEasy it would look something like this:

// arrange
var fileSystem = A.Fake<IFileSystem>();
A.CallTo(() => fileSystem.Exists("file.txt")).Returns(true);
A.CallTo(() => fileSystem.Exists("file1.txt")).Returns(false);
var sut = new SystemUnderTest(fileSystem);

// act
sut.DoSomething(); // do something that eventually saves file.txt

// assert
A.CallTo(() => fileSystem.Exists("file.txt")).MustHaveHappened();
A.CallTo(() => fileSystem.Exists("file1.txt")).MustHaveHappened();
A.CallTo(() => fileSystem.Save("file1.txt")).MustHaveHappened();
网友答案:

Um, well, if you want to verify the behavior of DoSomething(IDependency dependency) I'd say that you definitely want to verify that your dependencies are called.

That's what mocks are typically for. You want to be sure that your dependency is correctly dealt with in your SuT. You don't want to be bothered with implementation details of your dependencies.

So I nice test would be something like:

[Test]
public void DoSomething()
{
    var sut = new Whatever();
    var dependencyMock = MockRepository.GenerateMock<IDependency>();
    dependencyMock.Stub(() => mock.Method1()).Repeat = 1;
    dependencyMock.Stub(() => mock.Method2()).Repeat = 1;
    sut.DoSomething(dependencyMock);

    // verify that expected methods are being called
    dependencyMock.VerifyAllExpectations(); 

    // make some meaningful assert for sut.
    Assert. // ... etc
}

Note that I typed this without studio open, so all that modulo typos modulo bugs :). Just to give you an idea.


If you are only 'glueing' functionality together. Like a class does nothing else then call method on dependency objects, you it might be worthwhile to investigate if you should refactor that 'code smell'. But in the example you provided above, I'd say you got it all right, and you should go for it.

相关阅读:
Top