Unit test of Spring boot service with private fields and #PostConstruct - java

I have a Spring boot service defined like this
#Service
public class MyService {
private String field1;
private String field2;
#Autowired
private AnotherService anotherService
#PostConstruct
public void init() {
anotherService.initField1(field1);
anotherService.initField2(field2);
}
public String foo() {
return field1 + field2;
}
}
How should I write a unit test for foo. Well, it's more about how to deal with class fields and the PostConstruct methods.
Thanks!!
EDIT:
Added AnotherService as a field as well.

The following example shows a #Service Bean that uses constructor injection to obtain a required AnotherService bean:
#Service
public class MyService {
private String field1;
private String field2;
private final AnotherService anotherService;
public MyService(AnotherService anotherService) {
this.anotherService = anotherService;
this.anotherService.initField1(field1);
this.anotherService.initField2(field2);
}
public String foo() {
return field1 + field2;
}
}
Note you can omit the #Autowired becuase MyService has one constructor. See here for more info.
testing with Spring
Use the #RunWith(SpringRunner.class) and #SpringBootTest to inject MyService and start using it:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyServiceTest {
#Autowired
private MyService service;
#Test
public void testFoo() {
String expResult = "";
String result = service.foo();
assertEquals(expResult, result);
}
}
testing without Spring
public class MyServiceTest2 {
private MyService service;
#Before
public void setUp() {
service = new MyService(new AnotherService.Fake());
}
#Test
public void testFoo() {
String expResult = "";
String result = service.foo();
assertEquals(expResult, result);
}
}
Here Fake is a fake implementation of the AnotherService interface which allows you to have a pure unit test.

Writing good, testable code can be hard. There are some pitfalls waiting for everyone to fall into sooner or later.
As a rule of thumb, try to avoid field level injection, use constructor parameter injection instead:
#Service
public class MyService {
private AnotherService anotherService;
#Autowired
MyService (AnotherService anotherService) {
this.anotherService = anotherService;
}
}
This is the cleanest solution. You can call the constructor from your tests, spring will inject dependencies the same way at runtime. So there is no difference to deal with.
The same goes for any life cycle constructs like #PostConstruct. If you can avoid them, do it. Let the constructor handle it. If you absolutely have to keep them around, well, the only logical solution is to manually call them from your test code.
Now, how to setup services that at runtime would be autowired by the container?
For unit testing, you basically have three options (in no particular order):
If the required service is rather simple and can easily be constructed, create and pass it as the framework would do.
If the service has a limited interface that does not change too often, create a fake service.
Use a mocking lib like mockito (spring-boot-test provides it by default).

Related

Java/Spring: initMocks a class which got #Value from yml file

I am writing unit tests on a Service class like below :
#Component
#Profile({"default", "dev"})
public class MyService {
#Value("${my.property}")
private String property;
private OtherService otherService;
public void MyMethod() {
String myVar = otherService.method();
return String.format("MyVar %s & MyProperty %s", myVar, property);
}
}
My current test class for this test is like this:
#ActiveProfiles("dev")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyServiceTest {
#Mock
private OtherService otherService;
#InjectMocks
private MyService myService;
#BeforeEach()
public void init() {
MockitoAnnotations.initMocks(this);
when(otherService.method()).thenReturn("a string");
}
#Test
public void shouldReturnException() {
final Exception exception = assertThrows(ApiErrorException.class,
() -> myService.myMethod(var));
assertThat(exception).hasMessage("here the message");
verify(otherService, never()).method();
}
}
With this two classes, I have an application.yml & application-dev.yml to set my.property.
I want to get the property from the application-dev file during my tests execution.
But, with #InjectMocks, property is null. Whereas, using #Autowired in place of/with #InjectMocks, the property variable is set with the value present in file.
Problem, using Autowired with/in place of InjectMock results in the otherService variable being initialized, so no mock is created.
How can I still use Mockito, while having the property variable set with the value in the file?
I saw about ReflectionTestUtils.setField, but using it mean having no use of a yml file (which i am not fan).
Have a nice day
With the help of #Deadpool, the tests can use the values written in the application.yml file.
But, using #MockBean and #Autowired, the tests get a behavior I do not understand.
Example:
I test that a method return an Exception, and I verify that others methods are not called after the exception was catch: verify(otherService, never()).otherMethod();
Writing this line returns the following error org.mockito.exceptions.verification.NeverWantedButInvoked.
The initial exception is correctly caught, but the test does not seem to acknowledge that no other services must be called.
#SpringBootTest is used for integration testing which loads the ApplicationContext that will be utilized for test environment
The #SpringBootTest annotation can be used when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests.
Since you are generating integration environment using #SpringBootTest you need to mock the bean using #MockBean annotation
#ActiveProfiles("dev")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyServiceTest {
#MockBean
private OtherService otherService;
#Autowire
private MyService myService;
#BeforeEach
public void init() {
when(otherService.method()).thenReturn("a string");
}
}
I suggest you change your MyService class to accept the OtherService either via the constructor or via setter. Something like this:
#Component
#Profile({"default", "dev"})
public class MyService {
#Value("${my.property}")
private String property;
private OtherService otherService;
public MyService(OtherService otherService) {
this.otherService = otherService
}
public void MyMethod() {
String myVar = otherService.method();
return String.format("MyVar %s & MyProperty %s", myVar, property);
}
}
And then you do your test like this:
#ActiveProfiles("dev")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MyApplication.class)
public class MyServiceTest {
#Mock
private OtherService otherService;
#InjectMocks
#Autowired
private MyService myService;
#BeforeEach()
public void init() {
MockitoAnnotations.initMocks(this);
when(otherService.method()).thenReturn("a string");
}
}

Feign client unit test

I would like to know what is the best way to write unit tests in this context :
MyApi :
#RestController
public class MyApi{
#Autowired
MyAction myAction;
#PostMapping
public ResponseEntity addAction(#ResponseBody MyDto myDto){
return myAction.addAction(myDto);
}
}
MyAction :
#Service
public class MyAction{
#Autowired
private MyClient myClient;
public ResponseEntity<AuthenticationResponseDto> login(MyDto myDto{
return ResponseEntity.ok(myClient.addClient(myDto));
}
}
For example, is it mandatory to add constructor ?
Thanks
It's considered a good practice to use constructor injection, however if you don't want to use it you need to use #Mock and #InjectMocks. It uses reflection and constructor is not required to be defined.
#RunWith(MockitoJUnitRunner.class)
public class Test {
#Mock
private Client client;
#InjectMocks
private ServiceImpl plannerService = new ServiceImpl();
#Test
public void test() throws Exception {
....
}
}
I'm sure there is a way to avoid using an autowired constructor and just autowiring a field, however I use constructors as I consider it a good practice. It also makes it easy to inject a mocked object like so
#Mock
MyAction myAction;
MyApi myApi;
ResponseEntity<AuthenticationResponseDto> testResponse = ResponseEntity.ok
(new AuthenticationResponseDto());
#Before
public void setup(){
myApi = new MyApi(myAction);
}
#Test
public void simpleMyApiTestExample (){
when(myAction.login(any())).thenAnswer(i-> testRespone);
ResponseEntity<?> actualResponse = myApi.addAction(new MyDto());
assertThat(actualResponse).isSameAs(testResponse);
}
Just to give you an idea. I just wrote this example in the SO text editor, so appologies for any typos/mistakes. But hopefully this shows why having constructors is useful for testing things that are autowired. It allows you to mock the objects necessary for instantiation by adding them to the constructor. In this example this would probably also apply to MyDto and AuthenticationResponseDto objects as well.

Mockito Inject mock into Spy object

I'm writing a test case for a Class which has a 2 level of dependency injection. I use #Spy annotation for the 1 level dependency injection object, and I would like to Mock the 2nd level of injection. However, I kept getting null pointer exception on the 2nd level. Is there any way that I inject the mock into the #Spy object?
public class CarTestCase{
#Mock
private Configuration configuration;
#Spy
private Engine engine;
#InjectMocks
private Car car;
#Test
public void test(){
Mockito.when(configuration.getProperties("")).return("Something");
car.drive();
}
}
public class Car{
#Inject
private Engine engine;
public void drive(){
engine.start();
}
}
public class Engine{
#Inject
private Configuration configuration;
public void start(){
configuration.getProperties(); // null pointer exception
}
}
I've also wandered how to inject a mock into a spy.
The following approach will not work:
#Spy
#InjectMocks
private MySpy spy;
But the desired behavior can be achieved by a "hybrid" approach, when using both annotation and manual mocking. The following works perfectly:
#Mock
private NeedToBeMocked needToBeMocked;
#InjectMocks
private MySpy mySpy;
#InjectMocks
private SubjectUnderTest sut;
#BeforeMethod
public void setUp() {
mySpy = Mockito.spy(new MySpy());
MockitoAnnotations.initMocks(this);
}
(SubjectUnderTest here depends on MySpy, and MySpy in its turn depends on NeedToBeMocked).
UPD: Personally, I think that if you have to do such a magic too often, it might be a sign that there is something wrong with dependenicies between your classes and it is worth to perform a little bit of refactoring to improve your code.
The (simplest) solution that worked for me.
#InjectMocks
private MySpy spy = Mockito.spy(new MySpy());
No need for MockitoAnnotations.initMocks(this) in this case, as long as test class is annotated with #RunWith(MockitoJUnitRunner.class).
Mockito cannot perform such a tricky injections as it's not an injection framework. So, you need to refactor your code to make it more testable. It's easy done by using constructor injection:
public class Engine{
private Configuration configuration;
#Inject
public Engine(Configuration configuration) {
this.configuration = configuration;
}
........
}
public class Car{
private Engine engine;
#Inject
public Car(Engine engine) {
this.engine = engine;
}
}
In this case you have to handle the mocking and injection manually:
public class CarTestCase{
private Configuration configuration;
private Engine engine;
private Car car;
#Before
public void setUp(){
configuration = mock(Configuration.class);
engine = spy(new Engine(configuration));
car = new Car(engine);
}
#Test
public void test(){
Mockito.when(configuration.getProperties("")).return("Something");
car.drive();
}
}
I also met this issue during the unit testing with Spring boot framework, but I found one solution for using both #Spy and #InjectMocks
Previous answer from Yoory N.
#Spy
#InjectMocks
private MySpy spy;
Because InjectMocks need to have instance created, so the solution works for me is at below,
#Spy
#InjectMocks
private MySpy spy = new MySpy();
I think I just found the definitive answer. I tried Yoory approach but changed the order of the annotations :
#InjectMocks
#Spy
private MySpy spy;
I assume that Mockito first creates the mock, and adds a spy on top of that. So there is no need to instantiate the MySpy object.
Junit5 + mockito-junit-upiter-4.2.0 works well
#ExtendWith(MockitoExtension.class)
public class MyTest {
#Spy
#InjectMocks
private SomeClass spy = new SomeClass();

Injecting a String property with #InjectMocks

I have a Spring MVC #Controller with this constructor:
#Autowired
public AbcController(XyzService xyzService, #Value("${my.property}") String myProperty) {/*...*/}
I want to write a standalone unit test for this Controller:
#RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest {
#Mock
private XyzService mockXyzService;
private String myProperty = "my property value";
#InjectMocks
private AbcController controllerUnderTest;
/* tests */
}
Is there any way to get #InjectMocks to inject my String property? I know I can't mock a String since it's immutable, but can I just inject a normal String here?
#InjectMocks injects a null by default in this case. #Mock understandably throws an exception if I put it on myProperty. Is there another annotation I've missed that just means "inject this exact object rather than a Mock of it"?
You can't do this with Mockito, but Apache Commons actually has a way to do this using one of its built in utilities. You can put this in a function in JUnit that is run after Mockito injects the rest of the mocks but before your test cases run, like this:
#InjectMocks
MyClass myClass;
#Before
public void before() throws Exception {
FieldUtils.writeField(myClass, "fieldName", fieldValue, true);
}
Since you're using Spring, you can use the org.springframework.test.util.ReflectionTestUtils from the spring-test module. It neatly wraps setting a field on a object or a static field on a class (along with other utility methods).
#RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest {
#Mock
private XyzService mockXyzService;
#InjectMocks
private AbcController controllerUnderTest;
#Before
public void setUp() {
ReflectionTestUtils.setField(controllerUnderTest, "myProperty",
"String you want to inject");
}
/* tests */
}
You cannot do this with Mockito, because, as you mentioned yourself, a String is final and cannot be mocked.
There is a #Spy annotation which works on real objects, but it has the same limitations as #Mock, thus you cannot spy on a String.
There is no annotation to tell Mockito to just inject that value without doing any mocking or spying. It would be a good feature, though. Perhaps suggest it at the Mockito Github repository.
You will have to manually instantiate your controller if you don't want to change your code.
The only way to have a pure annotation based test is to refactor the controller. It can use a custom object that just contains that one property, or perhaps a configuration class with multiple properties.
#Component
public class MyProperty {
#Value("${my.property}")
private String myProperty;
...
}
This can be injected into the controller.
#Autowired
public AbcController(XyzService xyzService, MyProperty myProperty) {
...
}
You can mock and inject this then.
#RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest {
#Mock
private XyzService mockXyzService;
#Mock
private MyProperty myProperty;
#InjectMocks
private AbcController controllerUnderTest;
#Before
public void setUp(){
when(myProperty.get()).thenReturn("my property value");
}
/* tests */
}
This is not pretty straight forward, but at least you will be able to have a pure annotation based test with a little bit of stubbing.
Just don't use #InjectMocks in that case.
do:
#Before
public void setup() {
controllerUnderTest = new AbcController(mockXyzService, "my property value");
}
Solution is simple: You should put constructor injection for the object type while for primitive/final dependencies you can simply use setter injection and that'll be fine for this scenario.
So this:
public AbcController(XyzService xyzService, #Value("${my.property}") String myProperty) {/*...*/}
Would be changed to:
#Autowired
public AbcController(XyzService xyzService) {/*...*/}
#Autowired
public setMyProperty(#Value("${my.property}") String myProperty){/*...*/}
And the #Mock injections in test would be as simple as:
#Mock
private XyzService xyzService;
#InjectMocks
private AbcController abcController;
#BeforeMethod
public void setup(){
MockitoAnnotations.initMocks(this);
abcController.setMyProperty("new property");
}
And that'll be enough. Going for Reflections is not advisable!
PLEASE AVOID THE USAGE OF REFLECTIONS IN PRODUCTION CODE AS MUCH AS POSSIBLE!!
For the solution of Jan Groot I must remind you that it will become very nasty since you will have to remove all the #Mock and even #InjectMocks and would have to initialize and then inject them manually which for 2 dependencies sound easy but for 7 dependencies the code becomes a nightmare (see below).
private XyzService xyzService;
private AbcController abcController;
#BeforeMethod
public void setup(){ // NIGHTMARE WHEN MORE DEPENDENCIES ARE MOCKED!
xyzService = Mockito.mock(XyzService.class);
abcController = new AbcController(xyzService, "new property");
}
What you can use is this :
org.mockito.internal.util.reflection.Whitebox
Refactor your "AbcController" class constructor
In your Test class "before" method, use Whitebox.setInternalState method to specify whatever string you want
#Before
public void setUp(){
Whitebox.setInternalState(controllerUnderTest, "myProperty", "The string that you want"); }
If you want to have no change in your code then use ReflectionTestUtils.setField method
How do I mock an autowired #Value field in Spring with Mockito?

Injecting #Autowired private field during testing

I have a component setup that is essentially a launcher for an application. It is configured like so:
#Component
public class MyLauncher {
#Autowired
MyService myService;
//other methods
}
MyService is annotated with the #Service Spring annotation and is autowired into my launcher class without any issues.
I would like to write some jUnit test cases for MyLauncher, to do so I started a class like this:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
#Test
public void someTest() {
}
}
Can I create a Mock object for MyService and inject it into myLauncher in my test class? I currently don't have a getter or setter in myLauncher as Spring is handling the autowiring. If possible, I'd like to not have to add getters and setters. Can I tell the test case to inject a mock object into the autowired variable using an #Before init method?
If I'm going about this completely wrong, feel free to say that. I'm still new to this. My main goal is to just have some Java code or annotation that puts a mock object in that #Autowired variable without me having to write a setter method or having to use an applicationContext-test.xml file. I would much rather maintain everything for the test cases in the .java file instead of having to maintain a separate application content just for my tests.
I am hoping to use Mockito for the mock objects. In the past I have done this by using org.mockito.Mockito and creating my objects with Mockito.mock(MyClass.class).
You can absolutely inject mocks on MyLauncher in your test. I am sure if you show what mocking framework you are using someone would be quick to provide an answer. With mockito I would look into using #RunWith(MockitoJUnitRunner.class) and using annotations for myLauncher. It would look something like what is below.
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher = new MyLauncher();
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
The accepted answer (use MockitoJUnitRunner and #InjectMocks) is great. But if you want something a little more lightweight (no special JUnit runner), and less "magical" (more transparent) especially for occasional use, you could just set the private fields directly using introspection.
If you use Spring, you already have a utility class for this : org.springframework.test.util.ReflectionTestUtils
The use is quite straightforward :
ReflectionTestUtils.setField(myLauncher, "myService", myService);
The first argument is your target bean, the second is the name of the (usually private) field, and the last is the value to inject.
If you don't use Spring, it is quite trivial to implement such a utility method. Here is the code I used before I found this Spring class :
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
Sometimes you can refactor your #Component to use constructor or setter based injection to setup your testcase (you can and still rely on #Autowired). Now, you can create your test entirely without a mocking framework by implementing test stubs instead (e.g. Martin Fowler's MailServiceStub):
#Component
public class MyLauncher {
private MyService myService;
#Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
#Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
#Test
public void someTest() {
}
}
This technique especially useful if the test and the class under test is located in the same package because then you can use the default, package-private access modifier to prevent other classes from accessing it. Note that you can still have your production code in src/main/java but your tests in src/main/test directories.
If you like Mockito then you will appreciate the MockitoJUnitRunner. It allows you to do "magic" things like #Manuel showed you:
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
Alternatively, you can use the default JUnit runner and call the MockitoAnnotations.initMocks() in a setUp() method to let Mockito initialize the annotated values. You can find more information in the javadoc of #InjectMocks and in a blog post that I have written.
I believe in order to have auto-wiring work on your MyLauncher class (for myService), you will need to let Spring initialize it instead of calling the constructor, by auto-wiring myLauncher. Once that is being auto-wired (and myService is also getting auto-wired), Spring (1.4.0 and up) provides a #MockBean annotation you can put in your test. This will replace a matching single beans in context with a mock of that type. You can then further define what mocking you want, in a #Before method.
public class MyLauncherTest
#MockBean
private MyService myService;
#Autowired
private MyLauncher myLauncher;
#Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
#Test
public void someTest() {
myLauncher.doSomething();
}
}
Your MyLauncher class can then remain unmodified, and your MyService bean will be a mock whose methods return values as you defined:
#Component
public class MyLauncher {
#Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
A couple advantages of this over other methods mentioned is that:
You don't need to manually inject myService.
You don't need use the Mockito runner or rules.
I'm a new user for Spring. I found a different solution for this. Using reflection and making public necessary fields and assign mock objects.
This is my auth controller and it has some Autowired private properties.
#RestController
public class AuthController {
#Autowired
private UsersDAOInterface usersDao;
#Autowired
private TokensDAOInterface tokensDao;
#RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public #ResponseBody Object getToken(#RequestParam String username,
#RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
And this is my Junit test for AuthController. I'm making public needed private properties and assign mock objects to them and rock :)
public class AuthControllerTest {
#Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I don't know advantages and disadvantages for this way but this is working. This technic has a little bit more code but these codes can be seperated by different methods etc. There are more good answers for this question but I want to point to different solution. Sorry for my bad english. Have a good java to everybody :)
Look at this link
Then write your test case as
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
#Resource
private MyLauncher myLauncher ;
#Test
public void someTest() {
//test code
}
}

Categories