Unit testing code or glue code without real logic

My question will be in 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 are you going to test this class? I thought about this a bit and I identified a few options:

Test against returned car without mocking the builder
From an interface point of view, this is the best option, but it will not be an isolated unit test on VehicleRepository. I see no real advantage for testing VehicleRepository with VehicleBuilder, and not just with VehicleBuilder validation. VehicleBuilder can also have complex logic (for example, checking for accessories), which leads to several execution paths and which makes the VehicleRepository test very difficult.

Code refactoring to check linker status
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 state testing to test behavior, and the test is likely to be more robust, but I don't really like the resulting code (especially the getLuxuryCar () method, which actually does nothing).

Give up VehicleBuilder and check the behavior
I believe that such a unit test will be just a copy of the actual operation, verified, and will not add any real benefit. It would also be very fragile and completely dependent on the internal elements of the operation being tested. Probably unit test (using JMock) will look something like this:

 @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(); } 

Not unit test he
I am inclined to this option as I am not satisfied with any of the alternatives, and since the operations do not seem to require any testing. To clarify, I will still be unit test VehicleBuilder in isolation, but not VehicleRepository. However, this does not seem to be the prevailing opinion, which can be seen, for example, with this question: Device testing - what not to test

+4
source share
1 answer

I think that you have all your merits, and this is just a recommendation of what I will do.

An integration test will be more appropriate for this code using injected DAO objects that generate a data mockup or have a test data source.

However, your question is specific to unit testing, in which case I believe that testing against the returned vehicle object (without mocking the builder) is the best option. To avoid complications, I would continue simple tests on the returned vehicle (check that it is not null and possibly its “class invariants”), and not details about the type of vehicle (this test remains with the vehicle builder / manufacturer).

This is the advantage of the test:

  • If the car / builder later expands to require additional properties (for example, all vehicles must have brakes), but the developer neglects updating the repository, the test will not work.
  • It will act as a security test, providing at least code without exception.
+3
source

Source: https://habr.com/ru/post/1439399/


All Articles