2

I like working in languages with static types, because I like using types as a tool for designing an API before I start coding it.

I also like TDD, because it helps me concentrate on working in small steps to ensure I get consistent results.

But when I combine the two approaches, I often have this problem: I design the type of an API, but before I write unit tests for part of the functionality I find I must implement it because otherwise the compiler complains about the methods being incorrectly typed. For example, in a Java project, I have the following class:

 public class TransformedModelObserver<O,S>
 {
       private O sourceModel;
       private Function<O,S> transform;
       // note: a ChangeNotification<S> is a class that can only be constructed with a non-null instance of S
       private Consumer<ChangeNotification<S>> receiver;

       // ....

       /** Should call the receiver if and only if the source model change
        *  is visible in the transformed model.
        */
       public void notifySourceModelChanged ()
       {

       }
 }

I can simplify the test by using an identity function for the transform, which would allow for an easy first step, but the compiler complains if I don't call it anyway. So how would I work to implement this method in small test-driven steps in this scenario?

  • 1
    If you are not calling transform, which testcase caused it to be added to the class then? The idea of TDD is that you write only the minimum amount of code to get your current set of implemented testcases to pass. – Bart van Ingen Schenau Jan 2 '17 at 11:33
  • @BartvanIngenSchenau - the requirements of another method that is already fully implemented and that I've not included in the snippet. Also, while not specifically a test case for this class, the constructor's type (and hence the types of the fields shown) is dictated by its usage elsewhere in the project. – Periata Breatta Jan 2 '17 at 11:34
  • 1
    What language are you using, Java? In some languages it's common practice to leave momentarily unimplemented methods undefined. E.g. in Scala you'd write ??? in the method body (e.g. def myFunc() = ???); similarly, in Haskell you would write undefined in the function body. This will compile but throw an error if you actually try to execute the code. – Andres F. Jan 2 '17 at 14:16
  • 1
    @AndresF. - yes, this is Java. I'm familiar with the approach from my work in Haskell (which is also where I picked up the idea of designing the types before starting the implementation of the code), but I'm not sure how to apply it in Java, as there isn't really an equivalent of Haskell's undefined which can take any type. I could, as @RubberDuck suggests, write code that throws an exception, but it's hard to see how a partial implementation works at that point. – Periata Breatta Jan 2 '17 at 14:49
  • 1
    For the more mild cases for this, you can think of the compiler as your initial set of unit tests. On the other hand, if you find you need to implement a whole lot just to write a unit test of test suite, it could be an indication that your architecture needs rethinking. – Joel Cornett Jan 3 '17 at 7:20
1

Assuming that notifySourceModelChanged only gets called if sourceModel has actually changed (so, no filtering of unchanged model prior to transformation needed), then I can see these small steps to implement the function.

  1. Test that a changed model (with a transformation that doesn't hide any changes) always notifies receiver: Call receiver with a newly created S.
  2. Test that the model passed to receiver corresponds to the sourceModel after transformation: Replace the newly created S by one obtained from transform. Testcase uses an identity transformation for easy verification.
  3. Test that the receiver does not get notified if the transformation hides the change.

As you can see, I work around the type difference between O and S by breaking the correspondence between the models in the first (few?) testcase(s).

  • Ah, yes, interesting. My instinct was to approach it in the opposite order, which clearly doesn't work, but it does work this way round. Perhaps a general approach of reversing the order I'm considering writing the tests might help in such cases; it's certainly worth investigating. – Periata Breatta Jan 2 '17 at 14:48
3

I often take a similar approach when developing for .Net, which provides a NotImplementedException. I just throw it from any method that TDD hasn't forced me to implement yet. This keeps my tests failing after I get it compiling. I'm not sure if Java provides a similar exception class, but it certainly wouldn't be hard to create one.

Just a note about taking this approach to development: Be careful not to let your preconceived design take precedence over what your tests & code are telling you. If you feel like you're fighting the predesigned API, change it accordingly. The main benefit to your approach is that you get to think about your design more than either an up front design or TDD on their own. Don't lose that benefit because you're being stubborn.

  • 3
    Scala has a very nice pre-defined method for exactly this purpose, called ???. It makes for very nice reading: def notSureWhatThisShouldDoYet(foo: Int) = ???. ??? returns Nothing (which is a subtype of all types) and its implementation simply throws NotImplementedError. – Jörg W Mittag Jan 2 '17 at 15:03
-2

Don't start with types. Start with the test. Even write the Assert first, if you feel it allows you to hold back implementation a little longer.

The compiler will only complain at the Act step, when you begin using the System Under Test and it doesn't exist yet. Then most IDEs allow you to create class and method stubs on a simple keyboard shortcut.

In most cases you should be able to please the compiler with a simple UnsupportedException or return null in the generated method stub. Then you have a complete, compiling test before starting any real implementation of the inside of your method.

  • 3
    I find starting with the types helps me to get my ideas clear before I start writing the tests. Often, if I don't know the type of what I'm writing, it is often not clear to me what the first test even should be. Yes, I know this isn't pure TDD in the strict sense, but it seems to be a useful approach to me, except for this one issue that keeps cropping up... – Periata Breatta Jan 2 '17 at 15:01
  • Then maybe start with the Act section of the test? You will reason from the PoV of a consumer of the class, which to me is exactly the same thing as if you were the creator of the type writing it as an empty shell. – guillaume31 Jan 2 '17 at 15:04
  • Funny how downvoters can't be pissed to explain themselves... – guillaume31 Jan 3 '17 at 13:01
  • @guillaume31 the 3 upvotes on OP's comment may be a hint. – RubberDuck Jan 3 '17 at 22:50
  • @RubberDuck how exactly does that comment disprove anything I wrote? – guillaume31 Jan 4 '17 at 7:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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