views:

4270

answers:

3

Disclosure: I am a Rhino Mocks n00b!

OK, I know there has been a lot of confusion over the new AAA syntax in Rhino Mocks, but I have to be honest, from what I have seen so far, I like. It reads better, and saves on some keystrokes.

However, I am having problems getting my head around this, and seek some advice to make sure I am not missing something somewhere! :)

Basically, I am testing a ListController which is going to basically be in charge of some lists of things :) I have created an interface which will eventually become the DAL, and this is of course being stubbed for now.

I had the following code:

(manager is the system under test, data is the stubbed data interface)

    [Fact]
    public void list_count_queries_data()
    {
        data.Expect(x => x.ListCount(1));
        manager.ListCount();
        data.VerifyAllExpectations();
    }

Basically, as you can probably see, the main aim of this test is to just ensure that the manager is actually querying the DAL. Note that the DAL is not actually even there, so there is no "real" value coming back..

However, this is failing since I need to change the expectation to have a return value, like:

        data.Expect(x => x.ListCount(1)).Return(1);

This will then run fine, and the test will pass, however - what is confusing me is that at this point in time, the return value means nothing. I can change it to 100, 50, 42, whatever and the test will always pass?

This makes me nervous, because a test should be explicit and should totally fail if the expected conditions are not met right?

If I change the test to (the "1" is the expected ID the count is linked to):

    [Fact]
    public void list_count_queries_data()
    {
        manager.ListCount();
        data.AssertWasCalled(x => x.ListCount(1));
    }

It all passes fine, and if I switch the test on it's head to AssertWasNotCalled, it fails as expected.. I also think it reads a lot better, is clearer about what is being tested and most importantly PASSES and FAILS as expected!

So, am I missing something in the first code example? What are your thoughts on making assertions on stubs? (there was some interesting discussion here, I personally liked this response.

+1  A: 

I think it has to do with what your manager.ListCount() is doing with the return value.

If it is not using it then your DAL can return anything it won't matter.

public class Manager
{
    public Manager(DAL data)
    { 
        this.data = data
    }
    public void ListCount()
    {
        data.ListCount(1); //Not doing anything with return value
        DoingSomeOtherStuff();
    }    
}

If your list count is doing something with the value you should then put assertions on what it is doing. For exemple

Assert.IsTrue(manager.SomeState == "someValue");
Simon Laroche
+1 for the distinction between the expectation of the test double and the state-based testing
Mark Simpson
A: 

Have you tried using

data.AssertWasCalled(x => x.ListCount(1) = Arg.Is(EXPECTED_VALUE));
murki
+7  A: 

What is your test trying to achieve?

What behaviour or state are you verifying? Specifically, are you verifying that the collaborator (data) is having its ListCount method called (interaction based testing), or do you just want to make ListCount return a canned value to drive the class under test while verifying the result elsewhere (traditional state based testing)?

If you want set an expectation, use a mock and an expectation: Use MockRepository.CreateMock<IMyInterface>() and myMock.Expect(x => x.ListCount())

If you want to stub a method, use MockRepository.CreateStub<IMyInterface>() and myStub.Stub(x => x.ListCount()).

(aside: I know you can use stub.AssertWasCalled() to achieve much the same thing as mock.Expect and with arguably better reading syntax, but I'm just drilling into the difference between mocks & stubs).

Roy Osherove has a very nice explanation of mocks and stubs.

Please post more code!

We need a complete picture of how you're creating the stub (or mock) and how the result is used with respect to the class under test. Does ListCount have an input parameter? If so, what does it represent? Do you care if it was called with a certain value? Do you care if ListCount returns a certain value?

As Simon Laroche pointed out, if the Manager is not actually doing anything with the mocked/stubbed return value of ListCount, then the test won't pass or fail because of it. All the test would expect is that the mocked/stubbed method is called -- nothing more.

To better understand the problem, consider three pieces of information and you will soon figure this out:

  1. What is being tested
  2. In what situation?
  3. What is the expected result?

Compare: Interaction based testing with mocks. The call on the mock is the test.

[Test]
public void calling_ListCount_calls_ListCount_on_DAL()
{
   // Arrange
   var dalMock = MockRepository.Mock<IDAL>();
   var dalMock.Expect(x => x.ListCount()).Returns(1);
   var manager = new Manager(dalMock);

   // Act
   manager.ListCount();

   // Assert -- Test is 100% interaction based
   dalMock.VerifyAllExpectations();   
}

State based testing with a stub. The stub drives the test, but is not a part of the expectation.

[Test]
public void calling_ListCount_returns_same_count_as_DAL()
{
   // Arrange
   var dalStub = MockRepository.Stub<IDAL>();
   var dalStub.Stub(x => x.ListCount()).Returns(1);
   var manager = new Manager(dalMock);

   // Act
   int listCount = manager.ListCount();

   // Assert -- Test is 100% state based
   Assert.That(listCount, Is.EqualTo(1),
       "count should've been identical to the one returned by the dal!");
}

I personally favour state-based testing where at all possible, though interaction based testing is often required for APIs that are designed with Tell, Don't Ask in mind, as you won't have any exposed state to verify against!

API Confusion. Mocks ain't stubs. Or are they?

The distinction between a mock and a stub in rhino mocks is muddled. Traditionally, stubs aren't meant to have expectations -- so if your test double didn't have its method called, this wouldn't directly cause the test to fail.

... However, the Rhino Mocks API is powerful, but confusing as it lets you set expectations on stubs which, to me, goes against the accepted terminology. I don't think much of the terminology, either, mind. It'd be better if the distinction was eliminated and the methods called on the test double set the role, in my opinion.

Mark Simpson
+1 from me. Am also new to Rhino Mocks. Thanks for showing .Stub. I had been using .Expect because that was what all the examples used...
Edward