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.
@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.
@ 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.
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.
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.
Spring Official Document (@Value)
Spring Official Documentation (General Testing Utilities)
Recommended Posts