views:

128

answers:

1

In some cases its obvious when one or the other is warranted in other cases its not so clear. Take the following examples:

  1. A method which takes two inputs, performs a calculation and returns the result.
  2. A method which calls a factory object's Create method, passes the newly created object to the Add method of a collection object and returns void.
  3. A method which calls a collection's retrieval method and returns the retrieved object.

Now example 1 is pretty straightforward:

public int Calc(int int1, int int2)
{
    return int1 + int2 / int1 - int2 * int1;
)

There is no interaction with other objects so this clearly calls for state-based verification.

void CalcMethodShouldDoItsThing()
{
    int expected = 1;
    Calculator sut = new Calculator();
    int actual = sut.Calc(1,2);
    Assert.IsEqual(expected, actual);
}

Example 2 clearly causes state to change but its inaccessible without using behavior verification to observe the change.

void NewObject()
{
    _objectCollection.Add(_objectFactory.Create());
}

void NewObjectShouldCreateObjectAndAddItToCollection()
{
    IObjectFactory mockFactory = _mockery.NewMock<IObjectFactory>();
    IObjectionCollection mockCollection = _mockery.NewMock<IObjectionCollection>();

    // Mock expections for your mock framework of choice

    Objector sut = new Objector(mockFactory, mockCollection);
    sut.NewObject();
    _mockery.Verify();
}

Example 3 is a little more tricky. It returns a value it retrieved from another object so its not clear which verification technique is more appropriate.

myObject GetIt(int objectId)
{
    return _objectCollection.Retrieve(objectId);
}

All three of these examples are contrived but they serve to illustrate my question. The fact that example 2's test has the word "And" in it indicates its may be doing to much even though it is a single line of code. Example 3 is simple delegation of responsibility so some may argue it doesn't need to be tested at all but it was purposefully kept simple for this question. There are far more complex examples that would have illustrated the same point.

Before anyone bothers to suggest them... yes, I've read Fowler's essay on mockist vs. classist and Gerard Mesaros' excellent xUnit Test Patterns. Both author's voice their own personal preferences and present both sides of the coin but neither delves very deeply into what to do when the choice isn't so obvious.

+1  A: 

How about this for a way of thinking:

The thing under test has two kinds of relationships, with its caller and with its dependencies we always test both of these things.

Case 1: all we have are the relationships with the caller so we test with as many different inputs as we can think of. (including interesting things like inputs that might result in divide by zero)

Case 2: no inputs or outputs, everything is in the side-effects and so our tests make extensive use of the mocks (perhaps there are more tests than shown in the example.

Case 3: Our object under test has two responsibilities: it passes the input parameter to Retrieve() and it returns the value obtained from Retrieve(). Using mocks we can check both these things. It's only so simple because no processing or validation is done on either the id or the return type, if there was more, then it need more tests on the lines of Case 1.

In summary: test all the features of the method

djna
+1 to the summary
Dr. Xray