2

Just to get any confusion out of the way: I'm starting from the position where I already know what to mock, where and how to do it. I just don't know how to fit the mocking into the TDD process.

So let's say I have a my_function() already done and I want to write MyClass() that uses my_function() somewhere. Let's say my end goal is something like this:

class MyClass():
    (...)
    def some_method():
        var = my_function()
        self._do_something(var)

Writing this the classical way would be easy:

  1. Write the code.
  2. Realize that my_function() needs to be mocked because it does some connections and stuff.
  3. Write a my_function mock.
  4. Write a test for MyClass().some_method() using the mock.
  5. Run the test.

I want to do it test first though, but so far I've only came up with this:

  1. Write the test for MyClass().some_method(), ignoring the mock.
  2. Run the test - it fails.
  3. Write the code.
  4. Run the test - it fails because my_function() has some external dependencies and needs a certain environment etc.
  5. Write the mock and change the test.
  6. Run the test - it passes.

This approach seems flawed though. I write the test first so I can just run it again and again while writing code, waiting for it to go green. With my approach my test will never go green before I decide that I'm "done" with the code and only then start mocking (because I won't know what to mock before writing the code). Also I'm actually changing the test so it uses the mock, while the code is already "done" so I'm running the test to actually check whether I'm done changing the test, and not changing the code (I hope I'm making sense here).

What do you make of it? Is my TDD process flawed or is it ok and my reasoning about it is flawed? I tried searching the net but I haven't found anyone discussing or explaining this particular aspect. Either it's just mocking, assuming the code is already written, or it's TDD and the assumption is that you don't have any dependencies to mock.

2
  • Not sure I'm parsing the title correctly. Are you asking "when you should mock" or "when you're using TDD, if you should mock". Commented May 22, 2017 at 15:26
  • I'm asking "at what point in the TDD process should I actually write the code that does the mocking, provided that mocking is justified in this instance". Commented May 23, 2017 at 7:59

4 Answers 4

1

Of course you can't mock the dependency before you realize there's a dependency so in this regard what you are doing is fine. But you might ask yourself why it took you a test failing after writing the code to notice the dependency and what you could do about it.

You didn't notice the dependency because it was not explicit, you only discovered it when you ran the test. Conventional TDD wisdom says that at this point you should reconsider your design to make the dependency explicit, they call it dependency injection. Common ways to do it are passing my_function as an argument to MyClass.some_method or MyClass.__init__.

By using dependency injection you make tests easier to write and read:

  1. You missed the dependency because it was not obvious. Making it explicit makes it easier to take into account all needed dependencies when you are writing the test.

  2. You were mocking an implementation detail of MyClass.some_method. That could confuse other people reading your test, they would have to look at the implementation code to understand why you are creating an object that the test doesn't use. Dependency injection makes it easier to understand because the object is passed to a method call in the test.

0
1

Given that my_function "does connections and stuff", I guess it does so for a reason, which you verify in your test. So the moment you write an assert statement to verify the outcome, you probably already realize the code will need to make connections in order to make the test pass. If you do, it's OK to introduce the mock at that point.

If you don't realize it at that moment, you'll do at step 3 when you write the code. At that point you should only have to change the setup code of your test (to inject the mock and make it return an appropriate response for the call being done during the test). You shouldn't have to change the actual test itself.

In some cases returning an appropriate response (or doing whatever it takes to produce an outcome that can be verified) proves too difficult, and you'd rather just verify that the function has been called with correct arguments. In that case you can choose to deviate from the regular technique. It's all about trade-offs.

0

A key feature of a "UnitTest" is that is verifies the behavior of the tested unit in isolation.

Therefore you do not decide if mocking is needed by the complexity of my_function() but if it belongs to the same unit.

According to Roy Osherove (The Art Of UnitTesting) it belongs to the same unit if it has the same reason to change.

Because my_function() already exists (having its own set of unittests) it most likey has different reasons to change and (in consequence) is or belongs to a different unit.

Because it is/belongs to a different unit it must be mocked for the current test.

I'm asking "at which point in the TDD process should I actually write the mock and why". – iknownothing

You write the mock when creating the test as soon as you realize that communicating with a dependency (my_function()) is part of the desired behavior of the current unit. And the reason is to keep the current units behavior isolated for the test.

7
  • Hey Tim, thanks for clarification, although that's not really what I'm after. I agree with you here, but that's what I already know. I tried to be explicit about this at the beginning, but I guess I failed. I'm not asking whether it should be mocked or not. I already know it has to. I'm asking "at which point in the TDD process should I actually write the mock and why". Commented May 22, 2017 at 12:54
  • 1
    @iknownothing You're coming at it from the wrong direction. What the answer is suggesting is that you never "go back" and mock something. Either something is not part of the same unit and so is never not mocked, or something is part of the unit and is never mocked. This is why you wouldn't typically new something that goes and makes a network call, for example.
    – Phoshi
    Commented May 22, 2017 at 14:01
  • It's worth noting that the definition of "unit test" used in TDD is somewhat broader than the traditional "test only a single aspect of the code in strict isolation" definition that you're using. TDD does not require tests to strictly only test a single unit, and many early users of the technique do not practise strict isolation except where the dependencies are difficult to work with. See, eg, martinfowler.com/bliki/UnitTest.html
    – Jules
    Commented May 22, 2017 at 15:45
  • @Jules "the definition of "unit test" used in TDD is somewhat broader than the traditional "test only a single aspect of the code in strict isolation" definition that you're using." - Yes, the test only a single aspect paradigm is introduced by Roy Osherove in "the art of unittesting" and I think it really fits to TDD. Commented May 22, 2017 at 15:58
  • 1
    @TimothyTruckle Guess we'll have to agree to disagree. I find that adding additional unnecessary abstraction layers in order to support unit testing that aren't required for the code can cause unnecessary increase in complexity. This often leads to what David Heinemeier Hanson calls test-induced design damage and which prompted him to state that TDD is dead (before repenting when he realised that TDD can be done without unnecessary mocking).
    – Jules
    Commented May 23, 2017 at 6:42
0

I write the Assert part of my tests first, and never had any problems figuring out when to introduce mocks (or stubs), because it is a logical follow-up to what you're asserting on. Introducing fake objects usually comes up in the early stages of the Red phase.

  • In mock-based tests, you know you have to verify stuff recorded by the mock because what matters is that the external dependency it stands for was called correctly. So you write your Asserts first thing and jump to the Arrange part immediately after, to declare the mocks you just verified in the Assert part. Then you instantiate your class under test, somehow injecting it with the mocks, and complete the Act section.

    From there, everything compiles but the test is still red. Then you go on to write the production code that will make it pass.

  • Stubs, in contrast, are here to provide canned inputs. In tests that use them, something in the Asserts is usually linked to a value given by the stub. For instance you can stub out a Repository to return an arbitrary object A. In all likelihood, something in your assertions will be related to object A, otherwise you wouldn't bother setting up a stub returning it. Writing the assertions will lead you to using object A and thus force you to write the stub that returns it if you want the test to compile.

  • If a new dependency appears as a result of refactoring a class, the original test for the class should still work and cover everything you need. You can refactor your test to fake out the dependency and introduce another test dedicated to the new class if you want, though it is not an obligation - so that would be another legit time to introduce a mock, I guess.

Not the answer you're looking for? Browse other questions tagged or ask your own question.