TDD – Moving from Mock to Real Objects in Test-Driven Development

design-patternsmockingstubtdd

I'm like doing TDD so I started everything mocking objects, creating interface, stubbing, great.

The design seems to work, now I'll implement the stuff, a lot of the code used in the stubs are going to be reused in my real implementation yay!

Now should I duplicate the tests to use the real object implementation (but keeping the mocks object of the sensitive stuff like Database and "services" that are out of my context (http calls, etc…)) Or just change the mocks and stubs of the actual tests to use the real objects…….

So the question is that, keep two tests or replace the stubs, mocks?

And after that, I should keep designing with the mocks, stubs or just go with real objects?

(Just making myself clear I'll keep the mock object of the sensitive stuff like database and services that are out of my context, in both situations.)

EDIT

[Fact]
public void Change_status()
{
    var dbContext = MockRepository.GenerateStub<IDbContext>();
    var repository = MockRepository.GenerateMock<IRepository<Foo>>();

    var predicate = MockRepository.GenerateStub<Func<Foo, bool>>();
    repository.Expect(x => x.Get(predicate)).Return(_foo).Repeat.Once();
    dbContext.Expect(x => x.Commit());

    var ctl = MockRepository.GenerateStub<IFooCtl>();
    ctl.Stub(x => x.RejectRequest(_fooView)).WhenCalled(delegate
    {
        var foo = repository.Get(predicate);
        foo.Status = Status.Rejected;
        _fooView.Status = Status.Rejected.ToString();

    }).Return(true);

    ctl.RejectRequest(_fooView);

    repository.VerifyAllExpectations();
    dbContext.VerifyAllExpectations();
    Assert.True(_fooView.Status == "Rejected");
    Assert.True(result.Count() == 1);
}

This is a test, where I mock the repository, context and stub the facade. So what I'm trying to say is if I should change the stub of the facade to use the "real" code so this should be tested as

[Fact]
public void Change_status()
{
    var dbContext = MockRepository.GenerateStub<IDbContext>();
    var repository = MockRepository.GenerateMock<IRepository<Foo>>();

    var predicate = MockRepository.GenerateStub<Func<Foo, bool>>();
    repository.Expect(x => x.Get(predicate)).Return(_foo).Repeat.Once();
    dbContext.Expect(x => x.Commit());

    var ctl = new FooCtl(loanRepository, happyLoanDbContext);
    var result = ctl.RejectRequest(_loanView);

    repository.VerifyAllExpectations();
    dbContext.VerifyAllExpectations();
    Assert.True(_result);
}

Or if I should leave everything mocking and stubbing that tests the designs and create new ones that tests the implementation.

EDIT

Thanks for all your answer, I think I understand it better, I must know which is my SUT and test that, no mock or stub the SUT just the components that will help the SUT to pass the test

Best Answer

Or if I should leave everything mocking and stubbing that tests the designs and create new ones that tests the implementation.

A unit test never tests abstractions, or designs as you put it. It always tests one concrete implementation, that of the class under test.

In other words, if you have a test fixture for class A where you mock or stub out A's dependencies B and C, then you're not testing B and C's behavior or design. You're only testing A's behavior and that it is able to talk to B and C properly. In order to test concrete implementations of B and C, you have to create dedicated tests fixtures for both of them.

From a purely isolated unit test perspective, you neither need to substitute mocks for real implementations afterwards, nor to create integration test counterparts besides your unit tests (integration tests = tests involving several concrete implementations at the same time). Your isolated unit tests should stay as they are, with all the mocks and stubs. They stand on their own provided that you test all concrete implementations and their interactions with their dependencies.

As Erik points out, you could create integration tests, but keep in mind that they come at a cost, both in terms of processing time and maintenance time, and they shouldn't replace unit tests as your primary test harness IMO.

Related Topic