[JAVA] Tips for testing with mock for classes using @value

It is the 9th day of Tokyo University of Science Advent Calendar 2019.

This time, I will write about the method / tips for testing a class using the @Value annotation in the Java framework Spring.

What is @Value?

The @ Value annotation is an annotation used when you want to inject a value from another file. In case of Spring Boot If the default setting is used, it will be read and injected from the value described in application.yml or application.properties.

Specifically, it is in this format. First, write the value in application.yml.

application.yml


sample.threshold : 1

And in the argument of the annotation

@Value("${Key}")

Then, add @Value annotation to the fields and arguments in the class.

In particular

HogeCoordinator.java


@Service
public class HogeCoordinator {

  @value("${sample.threshold}")
  private Integer threshold;

  @Autowired
  private HogeService hogeService;

  public void hoge() {
    hogeService.fuga(threshold);
  }
}

It is such an image. If you write @ value ("$ {sample.threshold} ") in the field threshold of the HogeCoordinator class, 1 will be read from the key described in application.yml, and when the application is executed, it will be injected.

This has the advantage of being able to collect and manage application-specific values in a single file.

Testing for classes using @ Value

Then, it is a test method for the class using @Value of the main subject.

The test target is HogeCoordinator.java. When using @Value, if you write the following general test, the value will not be injected to the target to which @Value is given.

FailedHogeCoordinatorTest.java


public class FailedHogeCoordinatorTest {

  @InjectMocks
  private HogeCoordinator hogeCoordinator;

  @Mock
  private HogeService hogeService;

  @Before
  public void setup() {
    MockitoAnnotations.initMocks(this);
  }

  //Omitted below
}

Therefore, use the setField method of the ReflectionTestUnits class. In the method argument, specify the target instance, the field name to be injected, and the value to be injected.

In order to use this method, it is necessary to create an instance of HogeCoordinator, so keep it new.

If you write a test based on this, it will look like this.

SuccessHogeCoordinatorTest.java


public class SuccessHogeCoordinatorTest {

  @InjectMocks
  private HogeCoordinator hogeCoordinator = new HogeCoordinator();

  @Mock
  private HogeService hogeService;

  @Before
  public void setup() {
    ReflectionTestUnits.setField(hogeCoordinator, "threshold", 1);
    MockitoAnnotations.initMocks(this);
  }

  //Omitted below
}

By doing this, the value will be injected into the field with @Value.

The trick is that you need to create an instance of the class that injects the mock at test time, so it's better not to do constructor injection when doing DI.

The HogeCoordinator class that is the subject of this test is DI using @Autowired, but if you perform constructor injection using Lombok's @RequiredArgsConstructor as shown below, the arguments of the constructor will increase. This makes creating an instance cumbersome.

HogeCoordinator.java


@Service
@RequiredArgsConstructor
public class HogeCoordinator {

  @value("${sample.threshold}")
  private Integer threshold;

  private final HogeService hogeService;

  public void hoge() {
    hogeService.fuga(threshold);
  }
}

Therefore, when injecting into the class to be tested using the setField method of the ReflectionTestUnits class, it is easier to avoid constructor injection.

Summary

When testing against a class with @Value, use the setField method of the ReflectionTestUnits class to inject the value. At that time, if the DI of the class to be tested is set to a method other than constructor injection, it will be easier to create an instance during testing, and it will be easier to test.

at the end

I have a connection with OB this time, so I decided to participate in the Advent calendar. I am grateful to @piffett for providing a valuable place for me as I participated in the Advent calendar for the first time. Thank you very much.

References

Spring Official Document (@Value)

Spring Official Documentation (General Testing Utilities)

Recommended Posts

Tips for testing with mock for classes using @value
Notes for using BLE with iOS apps
Using Material Design for Bootstrap with Rails 5.2
Environment construction procedure for using PowerMock with JUnit
[Ubuntu 18.04] Environment construction for using PyTorch with RTX3090
Tips for using the Spotify app on Ubuntu
Tips for improving Jbuilder rendering time with jsonapi-serializer
Test code using mock with JUnit (EasyMock center)
tips for java.nio.file.Path
Testing with com.google.testing.compile