Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing mocks/stubs for an object before you have written the class for that object?

I'm designing a class that has two dependencies. One of the dependency classes has been written and tested. The other has not yet been written.

It has occurred to me because the remaining dependency will be written to facilitate the class that will use it, that I should write the latter first, and design the interface of the former as I go along, learning what it should do.

That seems to me to be a great way to make code. After all, as long as the main class gets a mock in its constructor, I can write it and test it without it being aware that its dependency doesn't exists, then I can create the dependency once I am sure I know what I need.

So: how do I do this? Create a skeleton class that I modify as I go along. Perhaps something like:

class NonExistantSkeleton
{
    public function requiredMethod1()
    {
    }

    public function newlyDiscoveredRequirement()
    {
    }
}

and then mock it using PHPUnit, and setting up stubs, etc, to keep my class under development happy?

Is this the way to go?

It seems like a nice way to develop code - and seems to me to make more sense than developing a dependency, without really knowing for sure how it's going to be used.

like image 632
Lewis Bassett Avatar asked Jan 18 '12 09:01

Lewis Bassett


2 Answers

In short:

Yes. At least thats what I'm doing right now.


Longer Version:

If the expected collaborators of your class don't exist at the point in time where you need them in your tests for the class you are building you have a few options:

  • Mock non existing classes (which phpunit can do)
  • Create class skeletions and mock those
  • Just create interfaces and get mocks for those (which phpunit can do too)
  • Maybe you don't need any of the above depending on the object

If you programm against an interface anyways than all you need to do is to create that interface and tell PHPUnit to create a stub/mock from it

  • +No new class without a test
  • +Using interfaces when appropriate is considered nicer/better than just hinting against classes

When mocking non existing classes you get some drawbacks that I don't like:

  • -High mock maintenance cost
  • -Chaning the methods on that classes is slow and tedious
  • -If you created the class you should rework the mocks again

so I'd advice against that.

The middle way would be to just create the empty class skeleton with its method and use those for mocking.

I quite like that way in cases where there is no interface to hint against as It is fast and creates stable test code.

Having barebone classes with public apis, for me, is no violation of TDD.


There are classes you don't need to mock.

Data transfer objects and Value Objects can always be created anywhere using the new in your production code so your tests also can just the the real objects.

It helps to keep your tests a little cleaner as you don't need to mock/expect a lot of getter/setter methods and so on.

like image 54
edorian Avatar answered Oct 10 '22 21:10

edorian


If you follow a test-driven development methodology then the usual approach is as follows:

  1. Figure out what your classes are meant to do, and what their public-facing APIs should be.
  2. Implement "empty" classes that consist of nothing but the public methods signitures with empty bodies (as you have done in the code example you gave).
  3. Work out an implementation strategy. This means working out which classes are dependant on each other and implementing them in an order that means that dependant classes aren't implemented until the classes it depends on are finished, or at least sufficiently functional to develop against. This means doing the classes with no dependencies first, then the classes that depend only on the completed classes, and so on.
  4. Write your tests. It's possible to write the tests now because you know what the black box for your classes look like, what they need to take as input and what they're supposed to return as output.
  5. Run the tests. You should get 0% success, but also 100% code coverage. This is now your baseline.
  6. Start to implement your classes according to your implementation strategy. Run your unit tests from time to time during this process, say once you've got a class completed, to make sure that it meets its specification as laid down in the unit test. Ideally, each test should show an increase in test passes whilst maintaining 100% code coverage.

EDIT: As edorian pointed out, PHP interfaces are a huge help here because PHPUnit can generate mocks and stubs from interfaces as well as from classes. They're also an excellent tool in reducing coupling and improving substitutability in general. They allow you to substitute any class that implements the expected interface, instead of just subclasses of the expected class.

like image 37
GordonM Avatar answered Oct 10 '22 22:10

GordonM