Testing the Units

In OO there’s levels of abstraction.  A class, for example, abstracts a read-world concept into a encapsulated bit of code.  A class is autonomous.  That class lives in world with other classes and interacts with them, but is autonomous.

I believe development testing should account for these abstractions, not just the interactions or behaviour of the system.  One problem I see with Test-Driven Development (TDD) and Behaviour-Driven Development (BDD) is that practitioners simply just center on interaction of the parts of the system and really don’t do any “unit testing”.  They get caught up in the mantras that are TDD and BDD and fail to see the trees for the forest and fall into testing by rote.  Unit testing tests individual units, the smallest testable part of an application[1].

Let’s look at the the BDD example on Wikipedia, where it tests the EratosthenesPrimesCalculator.  The behaviour that is tested in this example is basically the first prime number (which should be 2) the first prime number after 100 (which should be 101), the first prime number after 683 (which should be 691), and that the first 11 primes are correct.

The EratosthenesPrimesCalculator constructor interface accepts (or seems to) a signed integer.  The tests detailed only test 13 of 4,294,967,296 possibilities.  These tests may very well test the expected behaviour of one system, but don’t really test EratosthenesPrimesCalculator as a unit.  If the system only allows that behaviour, then these tests prove that it will work.  But, if at some point EratosthenesPrimesCalculator is used outside that behaviour (and that’s really the purpose of encapsulating code into classes: reuse) not much about EratosthenesPrimesCalculator has been validated.  At the very least the edge cases of EratosthenesPrimesCalculator() should be tested.  If there is a explicit contract that EratosthenesPrimesCalculator() it is to ensure, boundary cases should be included in that “very least”.  If they apply, corner cases should be pivotal to good unit testing.

I believe development testing should also be object-oriented as well, testing that individual objects work “as advertised”.  Testing interaction of classes is important, and TDD and BDD do that; but your system must have a solid foundation: it’s classes.

In relation to TDD and BDD, this testing will be done once the concrete implementations are done.  Depending on how you’ve designed your system; you could do this testing on an interface, then when concrete implementations are done, throw them at the test via the interface.

[1] http://en.wikipedia.org/wiki/Unit_Testing

8 thoughts on “Testing the Units

  1. Using a test-first approach, you first write a test for a method before you write the method. You only know conceptually how that method is going to be used in a user story (e.g. from TDD By Example, testMultiplication, which tests only that a particular multiplication with one value succeeded). Yes, your specific scenarios could be boundary cases; but one: that’s not usually what user stories describe, you’re usually testing interactions; two: implementation details like the limits of the “int” type (or “Integer” type in Java) isn’t a concern of the users; and three: it couples implementation details to the user stories (requirements artifacts).

  2. Hi Peter,

    TDD/BDD is part of the agile tool set. In order to be “agile” you only drive out code that you “need” as opposed to writing abstractions that model every conceivable detail. You only extend the abstraction when your stories dictate it, thereby helping you focus on functionality that is currently necessary and not “perceived” in the “future”. From experience this is a good thing. By only creating what you currently need you prevent yourself from over engineering solutions and creating code that will most probably be scrapped when the customer changes his mind. It keeps you agile.

    You may use interaction or state-based testing depending on the situation. You are not tied to interaction-based testing.

    Granted that vanilla unit tests don’t tests all scenarios. One reason for this is that unit tests need to execute in a very short time. If we ran test for all the scenarios, say the 4,294,967,296 possibilities in the EratosthenesPrimesCalculator – then our unit tests may never finish running by the end of the day. The longer it takes for something to run the less liked we are to run them. I do agree that unless we test all 4,294,967,296 possibilities that we are not sure that the code works. So we might use a tool like ScalaCheck (code.google.com/…/scalacheck)(if it existed in Java) to test all these possibilities. These could be run as  integration test, as we don’t run integration tests repeatedly in the TDD/BDD cycle of write a failing test, get it to pass and refactor. So essentially the “unit” tests need to be fast and lightweight and should test boundary cases and some probable inputs and expected outputs. Integration tests or acceptances tests could test all possibilities.

    Although user stories don’t cover “implementation details like the limits of the “int” type (or “Integer” type in Java) isn’t a concern of the users” we as developers should. We know the development platform and it is our duty to verify any assumptions made by the customer on the story cards. In an agile environment you should have access to a “customer” so any of these constraints should be made known to the customer so any changes can be made. (Maybe 4,294,967,296 is unnecessary or too little for the scenario)

    TDD/BDD are design tools as people have generally commented. But I do think we have a testing/speccing mindset while using these frameworks and therefore boundary cases and the  like have to be tested/specced because it leads to a better design. (eg. What happens when a null is passed into a method?  A good design would handle this although it is not on the story card)

  3. I agree, I feel more comfortable testing individual class methods and then building behaviour tests which could link several classes into a process.
    e.g: Given the user story ‘User must activate their account before they can login’, I would need to write a test which includes my membership class, email class, database class etc in order to test the entire process. I would write those tests after I’ve tested the granular parts (e.g: Save Registration Data).

  4. @sanj: I agree. I hope I didn’t come across as being anti-TDD or anti-BDD. My intention was to try to point out that using one of (TDD, BDD, Unit Testing) should not preclude all the others. I believe that they all should be part of your dev testing repertoire. Thinking TDD is all you need to do is not a good thing. Doing just TDD is better than no dev testing…

    Do BDD and TDD then follow up and unit test what you now know is needed/used. I don’t think user stores should be concerned with implementation-specific cases; but things like boundary cases should be included in dev testing.

    You start to dilute TDD and BDD if you couple implementation details into it; decouple that into a separate unit testing effort.

  5. @andrew: sure, test the granular parts as well. But your user story can be tested also, without the other parts. I like to use mock objects to really test the flow of a class (e.g. the presenter) and to seperate the it from the implementation of other classes. SO it doesn’t matter if SaveRegistrationData is really saving thinmgs as long as the presenter calls it. That imho is “testing the behaviour”. Imo the wiki article is no quite right. I consider “calculating the correct value” a functionality (Unittest) – not a behaviour…

  6. When you are in the corner and have got no cash to move out from that, you will require to take the personal loans. Just because it would aid you emphatically. I get bank loan every single year and feel myself fine just because of that.

Leave a Reply

Your email address will not be published. Required fields are marked *