Lives in the UK. Likes blogging, cycling and eating lemon drizzle cake. Roger is a DZone MVB and is not an employee of DZone and has posted 143 posts at DZone. You can read more from them at their website. View Full User Profile

Regular Unit Tests and Stubs - Testing Techniques 4

11.26.2011
| 4966 views |
  • submit to reddit

My last blog was the third in a series of blogs on approaches to testing code and discussing what you do and don’t have to test. It’s based around my simple scenario of retrieving an address from a database using a very common pattern:


...and I proffered the idea that any class that doesn’t contain any logic doesn’t really need unit testing. In this I included my data access object, DAO, preferring instead to integration test this class to ensure it worked in collaboration with the database.

Today’s blog covers writing a regular or classical unit test that enforces test subject isolation using stub objects. The code we’ll be testing is, again, the AddressService:

@Component
public class AddressService {

  private static final Logger logger = LoggerFactory.getLogger(AddressService.class);

  private AddressDao addressDao;

  /**
   * Given an id, retrieve an address. Apply phony business rules.
   *
   * @param id
   *            The id of the address object.
   */
  public Address findAddress(int id) {

    logger.info("In Address Service with id: " + id);
    Address address = addressDao.findAddress(id);

    address = businessMethod(address);

    logger.info("Leaving Address Service with id: " + id);
    return address;
  }

  private Address businessMethod(Address address) {

    logger.info("in business method");

    // Apply the Special Case Pattern (See MartinFowler.com)
    if (isNull(address)) {
      address = Address.INVALID_ADDRESS;
    }

    // Do some jiggery-pokery here....

    return address;
  }

  private boolean isNull(Object obj) {
    return obj == null;
  }

  @Autowired
  @Qualifier("addressDao")
  void setAddressDao(AddressDao addressDao) {
    this.addressDao = addressDao;
  }
}

Michael Feather’s book Working Effectively with Legacy Code states that a test is not a unit test if:

  1. It talks to a database.
  2. It communicates across a network.
  3. It touches the file system.
  4. You have to do special things to your environment (such as editing configuration files) to run it.

To uphold these rules, you need to isolate your object under test from the rest of your system, and that’s where stub objects come in. Stub objects are objects that are injected into your object and are used to replace real objects in test situations. Martin Fowler defines stubs, in his essay Mocks Aren’t Stubs as:

“Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'”.

Picking a word to describe stubs is very difficult, I could choose dummy or fake, but there are types of replacement object that are known as dummies or fakes - also described by Martin Fowler:
  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
However, I have seen other definitions of the term fake object,for example Roy Osherove in is book The Art Of Unit Testing defines a fakes object as:
  • A fake is a generic term that can be used to describe either a stub or a mock object...because the both look like the real object.
...so I, like many others, tend to call all replacement objects either mocks or stubs as there is a difference between the two, but more on that later.

In testing the AddressService, we need to replace the real data access object with a stub data access object and in this case, it looks something like this:

public class StubAddressDao implements AddressDao {

  private final Address address;

  public StubAddressDao(Address address) {
    this.address = address;
  }

  /**
   * @see com.captaindebug.address.AddressDao#findAddress(int)
   */
  @Override
  public Address findAddress(int id) {
    return address;
  }
}

Note the simplicity of the stub code. It should be easily readable, maintainable and NOT contain any logic and need a unit test of its own. Once the stub code has been written, next follows the unit test:
public class ClassicAddressServiceWithStubTest {

  private AddressService instance;

  @Before
  public void setUp() throws Exception {
    /* Create the object to test */
    /* Setup data that's used by ALL tests in this class */
    instance = new AddressService();
  }

  /**
   * Test method for
   * {@link com.captaindebug.address.AddressService#findAddress(int)}.
   */
  @Test
  public void testFindAddressWithStub() {

    /* Setup the test data - stuff that's specific to this test */
    Address expectedAddress = new Address(1, "15 My Street", "My Town",
        "POSTCODE", "My Country");
    instance.setAddressDao(new StubAddressDao(expectedAddress));

    /* Run the test */
    Address result = instance.findAddress(1);

    /* Assert the results */
    assertEquals(expectedAddress.getId(), result.getId());
    assertEquals(expectedAddress.getStreet(), result.getStreet());
    assertEquals(expectedAddress.getTown(), result.getTown());
    assertEquals(expectedAddress.getPostCode(), result.getPostCode());
    assertEquals(expectedAddress.getCountry(), result.getCountry());
  }

  @After
  public void tearDown() {
    /*
     * Clear up to ensure all tests in the class are isolated from each
     * other.
     */
  }
}

Note that in writing a unit test, we’re aiming for clarity. A mistake often made is to regard test code as inferior to production code with the result that it’s often messier and more illegible. Roy Osherove in The Art of Unit Testing puts forward the idea that test code should be more readable that production code. Clear tests should follow these basic, linear steps:
  1. Create the object under test. In the code above this is done in the setUp() method as I’m using the same object under test for all (one) tests.
  2. Setup the test. This is done in the test method testFindAddressWithStub() as the data used in a test is specific to that test.
  3. Run the Test
  4. Tear down the test. This ensures that tests are isolated from each other and can be run IN ANY ORDER.
Using a simplistic stub yields the two benefits of isolating the AddressService from the outside world and tests that run quickly.

How brittle is this kind of test? If your requirements change then the test and the stub changes - not so brittle after all?

As a comparison, my next blog re-writes this test using EasyMock.

1The source code is available from GitHub at:

git://github.com/roghughe/captaindebug.git

 

From http://www.captaindebug.com/2011/11/regular-unit-tests-and-stubs-testing.html

Published at DZone with permission of Roger Hughes, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Andras Hatvani replied on Sat, 2011/11/26 - 3:27am

Regarding the test method name:

  • "test" is redundant due to the @Test annotation (the prefix was only necessary with JUnit < 4)
  • it doesn't say what the result should be
  • the fact that you're using a stub (I stick to Fowler's definition and call such objects fakes) doesn't play any role and also doesn't have a business value

I'd use a name such as addressWillBefoundViaDatabaseQuery (sounds like a specification, doesn't it?)

Furthermore:

  • asserting the address object's properties shouldn't be in scope of this test, but in the DAO test
  • even in the DAO test the assertion should happen either in one extracted assertion method, or via an overridden equals.

Although this test class fulfills the criteria of unit tests, it verifies that the results of the DAO (whether real or stub or fake) will be used in the service class - this definitely is an integration. I personally use such kind of tests and call them unit tests, too, but some other name would be better.

Sebastian Gozin replied on Sat, 2011/11/26 - 7:49am

I agree with Adras that the example isn't very good to show of the advantages of TDD. I was actually similarly dissapointed with the previous examples as the question of just what should and shouldn't be tested usually suggests to me you are writing tests after and not before. A more use case or specification focussed approach would be much better.

The example used in this post could be greatly improved I think if it wasn't calling the same method on the service class as on the persistence gateway. Kind of funny how it suggests there could be some business logic but then a generic example is used.

 It's not like better examples are hard to find.

  • place order - business method
  • persists order details - business method delegates to persistence
  • send confirmation e-mail - business method delegates to mail system
  • tell store clerk to process the order - maybe also an e-mail...
  • return some data structure to allow user friendly feedback
  • deal with exception cases

Plenty of stuff to test which is useful, may allow you to write getters and or setters without having to write an explicit unit test for it and tells you something about what the system should do. Rather than having a test in order to have a test.

Finally, please get rid of the useless comments. I can tell you are using the new construct, the arrange-act-assert pattern in tests needs no explaining, and I really don't care you used the special case pattern. The code really does speak for itself. Or it would if you had actually written tests for it.

Roger Hughes replied on Sun, 2011/11/27 - 7:28am in response to: Andras Hatvani

Thanks for the comments. Firstly, yes I know that 'test' is redundant; however, many people still favour using it as a prefix and are familiar with test methods that start with the word 'test'.

Your next two points stray into the area of what I believe is behaviour driven development or BDD, which, I believe, states that the tests are your specifications. I'm in two minds about this: very descriptive test names are really useful, it's an idea put forward by many people including Roy Osherove in 'The Art of Unit Testing' and Misko Hevery in the Google Clean Code Tech talks. But, if code changes force a change in the unit test, the you'll need to change the test method name. This is not too big a deal except that, like comments, this often gets forgotten about.

Furthermore, there is the opinion (and this is not necessarily my opinion) that if the tests are your specification then you have a really crummy specification in terms of readability and usefulness. For example: when you use String.substring() do you look at the substring() method source code and its unit tests, or its javadoc?  Broaden that: when you need to plug into any library, API or project code framework, what is the specification.  I wager that most people would not answer the source or the unit tests.

People have also made the observation that, however well written they are, unit tests as a specification are:
  • not concise,
  • not structured,
  • lack any observations about how the method under test is intended to be used.
As I said, I like the idea of descriptive test names, but am undecided about BDD.

Next, I disagree here, if I'm testing the AddressService, then asserting the address object's properties is in scope of this test as I need to test what the AddressService returns. In that way I'll know whether or not its business logic has been applied correctly. Yes, this is a simple example purely aimed at demonstrating stubs, and the business logic of AddressService is pretty non-existent, but the idea is that the technique can be scaled up from being applied a trivial, contrived example to cater for real classes, where, for example, a real AddressService would do something useful.
Finally, feel free to stick to Martin Fowler's definition of the words fake and stub, but despite Martin Fowler’s notoriety, many people use other definitions, something that I think any author need to consider when writing about stubs, fakes, mocks etc

 

Roger Hughes replied on Sun, 2011/11/27 - 7:11am in response to: Sebastian Gozin

Sebastian, thanks for the comments. You are right in saying that the example isn’t a very good at demonstrating the advantages of TDD: it isn’t supposed to be. I’m currently covering the basics of writing unit tests and not how to do TDD. TDD is a totally different subject to simply advocating the writing of tests. I may cover how to do TDD later, but for now that’s beyond the scope of what I’m doing.

Next, does it really matter what the example scenario is? The code demonstrates how to write a unit test using a stub object and adding additional code in the form of fake/phony business logic to a specially contrived fake scenario would be redundant and only serve to complicate things.

Finally, the ‘useless’ comments have been specially put in to demonstrate to the blog’s intended audience how to layout a unit test. Obviously, you know all about writing tests, and as such have missed the point here: this blog isn’t aimed at people of your experience and ability, the point of this blog is to encourage people who either don’t know how to write tests, or don’t like writing tests, to write some tests and improve their code. There are a lot of people whole fall in to this category.

Sebastian Gozin replied on Sun, 2011/11/27 - 6:24pm

Thanks for the clarification.

I agree you have an example here of how to replace a complicated dependency with an easy to use stub. I'm not sure that's enough though...

As you say the point is to encourage people to give testing a chance. I know from experience they will use any excuse they can to avoid having to actually write tests. A very common one being that the examples given are too artificial or not realistic.

Hence the quality of our examples must improve and I understand it can be hard.

That said I did not think my place order suggestion earlier would have overly complicated things as it would have focussed on a business flow having left out all the details using stubs. The example would have been real and it would have shown how stubs saved you from dealing with an annoying database. You even hinted at the possibility with special e-mail handling stubs.

Another example, if were striving for clarity when writing unit tests then just what is the purpose of testFindAddressWithStub? Is it to find an address? Doesn't AddressDAO already do that? Is it to call a stub? Why didn't we deal with the special case in our tests? Given the example I actually think the special case is the most interesting bit.

I'm sorry if I sound overly critical. I do think people should share their experiences about testing.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.