Showing posts with label mock objects. Show all posts
Showing posts with label mock objects. Show all posts

Sunday, 9 June 2013

Code as you would order a burger



Recently during my work, I came across some code where the paradigm Tell Don't Ask came into play. You sometimes see code written that's not really using OO to its full strengths and still is of a procedural stance. There's often frequent examples where an object asks other objects for data and then does something with it. This is not really OO even though you may still be using other aspects of OO such as encapsulation, inheritance or polymorphism. It's still asking for data whereas the real value-add for OO is message passing or the behaviour of the objects rather than the data they may contain. I've come to understand this implicitly via the usage of mocks during testing. The use of mocks results in code that better follows the 'Tell, Don't Ask paradigm. One of the originators of mock objects noted that:

The trigger for our original discovery of the technique was when John Nolan set the challenge of writing code without getters. Getters expose implementation, which increases coupling between objects and allows responsibilities to be left in the wrong module. Avoiding getters forces an emphasis on object behaviour, rather than state, which is one of the characteristics of Responsibility- Driven Design - Tim McKinnon

Once I came over this mental hurdle, I found I wasn't primarily concerned with the data passed between the objects but really that the appropriate methods calls or methods (messages) between objects were observed. I used the Mockito mocking library and I find the most of my tests are interaction tests i.e. verifying that methods calls were made in the manner expected. State-based testing is still important but at the right place. This got me thinking about objects in terms of Alan Kay's emphasis on message passing http://c2.com/cgi/wiki?AlanKaysDefinitionOfObjectOriented. The following examples illustrate why telling an object to do something rather than asking it for its data is better.

I work a lot with enterprise integration and one of the examples that illustrates this point is message concatenation. During conversion from an internal message to one that that is supported by an external protocol. Dependent on whether a message is concatenated or not, then the message may be decorated with extra parameters to indicate that it is a multipart message rather than a single message.

In this example, the extra metadata parameters that get added are:
  • The message reference for the multipart message
  • The part number of the message
  • Total number of parts for the message
Implementation using "Ask" approach
A factory was used to create a map of parameters for message concatenation

Loading ....

The protocol converter that used this information was coded like this:

Loading ....


This bad for several reasons:
  • The MultipartMessageParameterFactory returns an abstract datatype. In this case a map, so users of this class have to know what keys to pull out the data. If this approach really must be followed, a domain object to hold the multipart parameters would be better, maybe a simple POJO with accessors for the different parameters.
  • The main problem with this approach is it results in Inappropriate Intimacy. The user is asking for some data and then assembling that data thereafter. The lookup keys for the map are made public presumably to mistakenly enforce DRY principles for magic strings within a unit test.
  • This class could become better by passing in the parameter factory in the constructor so it can be mocked. In this way, the unit test for this class will be simpler although it remains too complicated.

Implementation using "Tell" approach

The Tell is all about telling an object to perform some piece of work on your behalf rather than asking that object for data and then doing the work yourself. In the previous example, the protocol converter asked another object for the message concatenation info and then added those parts itself to the message. In the following example, we ask another object to do this bit of work for us. Firstly we introduce a domain object to replace the map use for the multipart parameters:
Loading ....
The factory now returns this POJO instead of the naked ADT:
Loading ....
Now the converter asks another object, MultiPartMessageUpdater, to do the message updating:
Loading ....
The code for updating the message is pushed into its own class. As well as enforcing the single responsibility principle, it also endorses the command and query separation principle so that the retrieval of the actual parameters (query) is separated from the addition of those parameters to the message itself (command).
Loading ....
As a byproduct, the test for the converter becomes very simple. Beforehand we would have to test every permutation of concatenated parameters. Now because code has been pushed to their logical place the test boils down to just testing the following behaviour:
  • Message should be updated if message is part of a multipart message
  • Message should be untouched if message is just a single message

Loading ....
The permutations for those 3 message parameters are pushed to where it should be namely the test for the MultipartMessageParameterFactory. Each test then tests what it should instead of testing the unit and all of its dependencies' behaviour at the same time resulting in a combinatorial explosion of test methods making the test unclear.

Conclusion

  • We've now got a class which now asks other classes do work i.e. operation requests instead of asking for data and doing the work ourselves.
  • By following this approach the single responsibility principle naturally falls out. Each class performs a single well, understood operation and only that operation. This makes those classes loosely coupled and become candidates for reuse in different contexts if needed.
  • Unit tests have become clearer and focused as they only testing a single object rather than a multitude. If you notice I've endeavoured to use constructor injection for any dependencies. This makes the testing of classes easier as I can mock out any external dependencies.
I remember this paradigm by thinking what you would do if you ordered a burger in McDonalds. Would you ask the server for two sliced bun halves, some mayo, lettuce, a hamburger, cheese and tomato sauce or would you just ask for a Big Mac? :D
Links


Wednesday, 25 July 2012

Constructors that do too much


A flaw I've notice frequently in a legacy code base is constructors which are doing too much work. A common anti-pattern is that a configuration is passed into a constructor. This configuration is then perused to find information so that further objects can be constructed in the constructor. I will discuss why I think this is bad practice.

As an example I have a message dispatcher which delegates to a sender object
to send a message to a destination. A dispatcher usually makes a decision on the appropriate destination based on the message. For this example there is only one endpoint. This destination is to be read from the configuration.

So I have an interface for the sender:


and a dispatcher which uses the configuration to build a sender inside its constructor:


The expected usage for the constructor then becomes:

This is bad on several counts:

  • Abstract data type is passed in. There's no indication how this will be used or what it's used for. It's just a bucket of parameters. If possible a specific type should be used in preference to an ADT. It's all about the domain and context. Check out Domain Driven Design  or GOOS touches on this point.
  • Harder to unit test. I have to make sure configuration is correct so that internal objects can be created properly. In my example I've only one object to consider but they may be several. 
  • I can't mock the behaviour of any objects created by the constructor. In essence that behaviour is hard-wired which may be deleterious to testing if that behaviour is using a real resource i.e. socket or database. It turns the unit test into more of an integration test

Use a factory approach


So instead of passing in a configuration, pass in the already constructed objects. A DI framework such as Spring or Guice will assist you in this regard as it's fundamentally what DI is all about. You inject pre-configured objects into other objects rather than building those objects internally.

If you're not using a DI framework (I'm in this situation as it's legacy code) then it's better to pass a factory which builds the object on the fly as a constructor parameter.

To illustrate this:

I can have a factory that produces Sender objects. I've taken the liberty of introducing a generic factory for this purpose. It builds objects of the specified type for a given configuration.



Now I provide an implementation for the Sender:

And to illustrate the point I rewrite the Sender class:


Finally I rewrite the message dispatcher. The constructor now takes objects rather than configuration


And the usage becomes:



This is a lot better:


  • Now the dispatcher only knows about objects. It does not even know a configuration is used.
  • The transformation of a configuration into object is localized in the factory and not spread out across the code base
  • The class becomes trivial to unit test. Instead of worrying about configurations I can simply create a mock Sender and pass that to the dispatcher. In this way I can focus solely on the behaviour of the Dispatcher itself.


Conclusion

Resist the urge to pass around configuration objects and allowing constructors to build new objects. A constructor should ideally be used for assembly only. It's better to just pass pre-configured objects into a constructor. This makes the intention of that constructor clear and makes it easier to unit test the class in question as the colloborators can be mocked easily.

Wednesday, 1 June 2011

JUnit Theories to the Rescue

I recently developed a restart manager to respond to JMS connection outages. After the connection was restored, I needed to reinitialize my message clients in order for them to rebuild their JMS sessions. But I needed to test that a restart only occurred for particular status transitions. At first I thought this would be an onerous task as I would have to work out all the status permutations. That is until I came across JUnit Theories.

JUnit Theories
Theories allows one to write tests that apply to a (potentially infinite) set of data points rather than having to recreate the same test multiple times with different data or creating one test and iterating through your own collection of data values.
Here is an example:

Messaging client
First off is the interface for the messaging client. It has two lifecycle methods, start and stop which are self-explanatory, as well as methods for reading and sending a message which for brevity are just strings.
Status Enum
The following enum represents the status of the JMS connection. As can be seen there are numerous states so it would be cumbersome to work out all the possible combinations for each status transition. This is where Theories become so useful. More of that later.

Restart Manager
This class is used to restart a message client when it recognizes a status transition from FAILED to STARTED.
Test class
Finally here's the test class. Notice I'm using mock objects here using Mockito. As I'm programming to the interface of the messaging client not its implementation, I can make use of a mock and verify its behaviour.  Presently as of JUnit 4.8, theories is still within an experimental package.
The most important bit of configuration is @Datapoints annotation. This sets up the data that'll be pass into the test methods. In this test I have two theory methods. One theory checks that the message client is restarted when there is a suitable status transition i.e. FAILED to STARTED. Another theory checks that the message client is not restarted if the status transition is not suitable. i.e. not FAILED to STARTED.
When you run the tests you'll find the all the combinations of the Status enums are passed to the test methods in question as shown below:
In this way, I ensure that all possible transitions are covered by my unit tests and the restart manager's behaviour is as required.
Conclusion
The use of theories allows tests to be devised that cover all possible combinations of data. This is in contrast to parameterized tests where the dataset to be passed to a test is strictly defined and the onus is on the developer to work out what data is needed for a particular range of tests. Each approach can be used in different situations as dictated by your requirements.