In Spring Framework (boot), how do you implement it without modifying the injection class at all while trying to make it "testable code" which is the merit of DI? I thought about it, and I thought about it myself, so I would like to write about its contents.
What is Spring? What is DI? I won't talk about it. Here is a nice explanation, so please refer to it. I also participated in the study session mentioned at the beginning, but it was a good study.
Even if you make the code testable, even if you create a stub class for testing because the class to be injected is not implemented, you have to rewrite at least the injected part when that class is completed. I was thinking.
For example, like this.
@RestController
public class DemoController {
@Autowired
DemoServiceStub demoService;
//DemoService demoService; //Switch commented out here for testing
@GetMapping("/echo")
public String echo() {
return demoService.getMessage();
}
}
public class DemoService {
String getMessage(){
//Still being implemented
return "xxx";
}
}
public class DemoServiceStub {
String getMessage(){
return "foo is stub";
}
}
As I wrote in the comment, I have to switch the injection part between the regular class and the stub class. If you want to test only a specific method, it will not affect you, but after all it feels unpleasant to play with the field.
So, here is what I thought about and fixed.
@RestController
public class DemoController {
@Autowired
@Qualifier("demoservice") //Specify the name of the injection target. Do not switch between regular and stub.
DemoService demoService;
@GetMapping("/echo")
public String echo() {
return demoService.getMessage();
}
}
//Make one interface
public interface DemoService {
String getMessage();
}
@Service(value = "demoserviceWIP") //Give it a name. Don't wear it with a stub.
public class DemoServiceImpl implements DemoService {
public String getMessage() {
//Still being implemented
return "xxx";
};
}
@Service(value = "demoservice") //Give it a name
public class DemoServiceImplStub implements DemoService {
String getMessage(){
return "foo is stub";
}
}
This eliminates the need to tweak the source under test (DemoController in this case) for both legitimate and stub. I feel that this method is also effective when there are multiple classes that I want to implement in the interface. In Spring, if there are multiple types (classes) that register beans, it is not possible to determine which one should be registered, so it seems that name resolution is done in this way. (Rather, it feels like I got a hint for this case from this mechanism.)
--Implement using an interface to make the regular class and the stub class common
――I feel that this should be the minimum as a DI in the first place.
--Give an alias to the injected class (@Service
value attribute)
--Resolve the name on the injection side (@Qualifier
)
--The injection side does not make any changes regardless of the implementation status of the injection side.
--Switch the injection name on the injection side
It is a method that hardly came out even if I googled. It may be too obvious or it may be an anti-pattern. In that case, please point it out.
Recommended Posts