TDD – Correct Way to Work with Test-Driven Development (TDD)

mockingtdd

I'm trying to grasp what's the idea behind TDD and how a team is supposed to work with it. I have the following test case with NUnit + Moq (just writing by memory, it is not assured the example compiles but it should be explanatory):

[Test]
public void WhenUserLogsCorrectlyIsRedirectedToLoginCorrectView() {
    Mock<IUserDatabaseRepository> repoMock = new Mock<IUserDatabaseRepository>();
    repoMock.Setup(m => m.GetUser(It.IsAny())).Returns(new User { Name = "Peter" });        

    Mock<ILoginHelper> loginHelperMock = new Mock<ILoginHelper>();
    loginHelperMock.Setup(m => m.Login(It.IsAny(), It.IsAny())).Returns(true);
    Mock<IViewModelFactory> factoryMock = new Mock<IViewModelFactory>();
    factoryMock.Setup(m => m.CreateViewModel()).Returns(new LoginViewModel());

    AccountController controller = new AccountController(repoMock.Object, loginHelperMock.Object, factoryMock.Object)

    var result = controller.Index(username : "Peter", password: "whatever");

    Assert.AreEqual(result.Model.Username, "Peter");
}

AccountController has 3 dependencies which I mock that, when orchestrated inside the controller, allows me to verify if a login was correct or not.

What stumbles my mind is that… if in TDD in theory you have to write first your test suite and build your code up from it, how am I supposed to know beforehand that in order to perform my operation I'll need to use those three dependencies and that the operation will call certain operations? It's like I need to know the innards of the Subject Under Test before even implementing it in order to Mock the dependencies and isolate the class, creating some kind of write test – write code – modify test if necessary cycle.

Naturally, without any knowledge of the innards of my code and just expressing the test, I could express it like it could just need the ILoginHelper and "magically" suppose before writing the code that it will return the user on a successful login (and ultimately realize that the underlying framework doesn't work that way, for instance returning just an ID instead of the full object).

Am I understanding TDD in an incorrect way? Which is a typical TDD practice on a complex case?

Thank you

Best Answer

if in TDD in theory you have to write first your test suit and build your code up from it

Here is your misunderstanding. TDD is not about writing a full test suite first - that's is a false myth. TDD means to work in small cycles,

  • writing one test at a time
  • implement only as much code as needed to make the test "green"
  • refactor (the code and the tests)

So the creation of a test suite is not done in one step, and not "before the code is written", it is interwoven with the implementation of the code in stake.

Applied to your example: you should try to start with a simple test for a controller without any dependencies (something like a prototype). Then you implement the controller, and refactor. Afterwards you either add a new test which expects your controller to do a little bit more, or you refactor/extend your existing test. Then you modify your controller until the new test becomes "green". That way you start with a simple combination of tests & subject under test, and end up with a complex test & subject under test.

When going this route, at some point in time you will find out what additional data you need as input for the controller to do its work. This can indeed happen at a moment where you try to implement a controller method, and not when designing the next test. That is the point where you stop to implement the method for a short time, and start introducing the missing dependencies first (maybe by a refactoring of your controller's constructor). This leads straightforward to a refactoring of your existing tests - in TDD, you will typically first change the tests calling the constructor, and add the new constructor attributes afterwards. And that's where coding and writing of the tests become completly entangled.

Related Topic