Testing software is one of the primary roles of software developers. In the developer's day to day, tests can use up as much time as any other activity (including writing features). Yet, so little seems to be known about the properties of tests. What qualifies as a good test? What classifications are there? What does tool X accomplish in testing? These are big questions that I may take a stab at later. This post is going to focus on one area of testing: unit tests and fake objects.

Unit Tests

Unit tests, from the perspective of mocking, fall into two categories: stateful and behavioral. Sometimes a unit test will be both (though we should avoid this) but it cannot be neither. Knowing the distinction between these two will help you with your mocks.

Stateful unit tests verify the state of the target object after internal modification. An example:

var receipt = new Receipt();
receipt.addLineItem(new LineItem('Watermelon', 5.00));
receipt.addLineItem(new LineItem('Mango', 2.00));
Assert.areEqual(7.00, receipt.subtotal());

Here we can see that the end result of subtotal (7.00) is what is being tested. We aren't verifying the contract between LineItem and Receipt.

Behavioral unit tests verify that the behavior of the object is as expected. An example:

var receipt = new Receipt();
var watermelonLineItem = new MockLineItem();
receipt.addLineItem(watermelonLineItem);

watermelonLineItem.expect('price').called(1).return(5);

receipt.subtotal();

watermelonLineItem.verify();

Here we can see that watermelonLineItem's contract is being tested. We are not verifying the state of the object after execution.

Mocking Techniques

There are a variety of mocking techniques. However, these can be relatively cleanly divided by the stateful/behavioral classification of unit tests. For stateful unit tests, stubs or fakes can be used. For behavioral unit tests, mocks or spies are used.

Stub and fakes are test stand-ins. They are not the primary engines of the test and only serve a supporting role. An example:

var lineItem = stub();
stub.when("price").return(15);
var receipt = new Receipt();
receipt.addLineItem({"price":function(){ return 5; }}); // a fake object
receipt.addLineItem(stub); 
Assert.areEqual(20, receipt.subtotal());

Mocks and spies are the primary engines of the test. They are verifying that the contracts between the objects are being met. An example:

var receipt = new Receipt();
var watermelonLineItem = new MockLineItem();
var peachLineItem = spy(new LineItem("peach", 5));
receipt.addLineItem(watermelonLineItem);
receipt.addLineItem(peachLineItem);

watermelonLineItem.expect('price').called(1).return(5); // a mock
peachLineItem.expect('price').called(1); // a spy (delegates to base object)

receipt.subtotal();

watermelonLineItem.verify();
peachLineItem.verify();

This is not completely black and white. Sometimes you want a stand in (read: stub) in your behavioral tests in order to get around some hard dependencies quickly.

Which to use?

Behavioral or stateful?

I think that both are valid and have their places in a testing suite. However, we should be disciplined about test names and do not mix behavioral and stateful methods into a single test. Example test names for the final two tests:

shouldReturn20WhenGivenLineItemsOfValue15And5
shouldCallPriceOnLineItemsWhenCalculatingSubtotal