My question will be at the hand of a simplified example (in Java):
public class VehicleRepository {
// DAOs responsible for fetching objects from data store (injected)
private IChassisDAO chassisDAO;
private IEngineDAO engineDAO;
private IWheelDAO wheelDAO;
private IAccessoryDAO accessoryDAO;
public Vehicle getLuxuryCar() {
VehicleBuilder builder = getVehicleBuilder();
builder.setChassis(chassisDAO.findBySize(ChassisSize.NORMAL));
builder.setEngine(engineDAO.findByPower(EnginePower.HIGH));
builder.setWheelTemplate(wheelDAO.findBySize(WheelSize.NORMAL));
builder.setAccessories(accessoryDAO.findByType(Accessory.LUXURY));
return builder.build();
}
public Vehicle getOffroadCar() {
VehicleBuilder builder = getVehicleBuilder();
builder.setChassis(chassisDAO.findBySize(ChassisSize.LARGE));
builder.setEngine(engineDAO.findByPower(EnginePower.HIGH));
builder.setWheelTemplate(wheelDAO.findBySize(WheelSize.LARGE));
return builder.build();
}
// Similar operations ...
}
How would you go about unit testing this class?
I’ve thought on this for a bit, and I’ve identified a few options:
Test against the returned Vehicle without mocking the builder
From an interface perspective this is the best option, but it won’t be an isolated unit test on VehicleRepository. I can’t see any real advantage to testing the VehicleRepository along with the VehicleBuilder as opposed to only testing the VehicleBuilder. The VehicleBuilder might also have complex logic (such as checks against which Accessories are present), which leads to multiple execution paths and which would make VehicleRepository’s test very complex.
Refactor the code in order to test the state of the builder
An example would be splitting the getLuxuryCar operation into two operations:
public Vehicle getLuxuryCar() {
VehicleBuilder builder = getLuxuryCarBuilder();
return builder.build();
}
// Visible for testing in the same package
protected VehicleBuilder getLuxuryCarBuilder() {
VehicleBuilder builder = getVehicleBuilder();
builder.setChassis(chassisDAO.findBySize(ChassisSize.NORMAL));
builder.setEngine(engineDAO.findByPower(EnginePower.HIGH));
builder.setWheelTemplate(wheelDAO.findBySize(WheelSize.NORMAL));
builder.addAccessory(accessoryDAO.findByType(Accessory.LUXURY));
return builder;
}
I prefer the state testing to behaviour testing, and the test will probably be more resilient, but I’m not very fond of the resulting code (especially the getLuxuryCar() method which doesn’t really do anything).
Mock the VehicleBuilder and test the behaviour
I believe that such a unit test will just be a replica of the actual operation being tested, and won’t add any real benefit. It would also be very fragile and completely dependent on the internals of the operation being tested. A likely unit test (using JMock) would be something like:
@Test
public void shouldBuildLuxuryCar() {
context.checking(new Expectations() {{
oneOf(chassisDAOmock).findBySize(ChassisSize.NORMAL);
oneOf(vehicleBuilderMock).setChassis(with(any(Chassis.class)));
oneOf(engineDAOmock).findByPower(EnginePower.HIGH);
oneOf(vehicleBuilderMock).setEngine(with(any(Engine.class)));
oneOf(wheelDAOmock).findBySize(WheelSize.NORMAL));
oneOf(vehicleBuilderMock).setWheelTemplate(with(any(Wheel.class)));
oneOf(accessoryDAOmock).findByType(Accessory.LUXURY));
oneOf(vehicleBuilderMock).setAccessories(with(any(Set.class)));
oneOf(vehicleBuilderMock).build();
}});
context.assertIsSatisfied();
}
Don’t unit test it
I’m leaning towards this option as I’m not happy with any of the alternatives, and as the operations don’t seem to really require any testing. To clarify, I’ll still unit test the VehicleBuilder in isolation, but not the VehicleRepository. This doesn’t seem to be the prevailing opinion however, as can be seen for example with this question: Unit Testing – What not to test
I think your points all have merit, and this is merely a recommendation of what I would do.
An integration test would be more appropriate for this code, using injected DAO objects that generate mock data or have a test data source.
However your question is specific to unit testing, in which case I think testing against the returned vehicle object (without mocking the builder) is be the best option. To avoid complexity I would keep to simple tests on the returned vehicle (check it is not null and perhaps its’ class invariants) as opposed to details about the type of vehicle (this testing is left to the builder/producer of the vehicle).
This advantages of the test would be: