can we use #Autowired without bean registration? - java

In the code below, surprisingly, when I use #AutoWired, the fields are set but this class has not been registered as a bean, and the program works correctly, but when I took the MockingTest bean from the context, it said that there is no such bean. Is it possible to use AutoWired without registering a class as a bean?
#SpringBootTest
public class MockingTest {
#Autowired
private ApplicationContext context;
#Autowired
private CollegeStudent collegeStudent;
#Autowired
private StudentGrades studentGrades;
#Mock
private ApplicationDao applicationDao;
#InjectMocks
private ApplicationService applicationService;
#BeforeEach
void setUp() {
collegeStudent.setFirstname("Al");
collegeStudent.setLastname("Zam");
collegeStudent.setEmailAddress("555#gmail.com");
collegeStudent.setStudentGrades(studentGrades);
}
#Test
#DisplayName("mockito testing")
public void testMocking() {
when(applicationDao.addGradeResultsForSingleClass(studentGrades.getMathGradeResults()))
.thenReturn(100.0);
assertEquals(100.0, applicationService.addGradeResultsForSingleClass(studentGrades.getMathGradeResults()));
verify(applicationDao).addGradeResultsForSingleClass(studentGrades.getMathGradeResults());
verify(applicationDao,times(1)).addGradeResultsForSingleClass(studentGrades.getMathGradeResults());
}
}

#SpringBootTest sets up multiple hooks into the test runner. One of them is to inject beans into #Autowired fields, without the test class itself being a bean.
#SpringBootTest actually does a lot of "magic" behind the scenes, just so that things just work like we might expect, without us thinking about them too much.

Related

Mocking repository function leads to null pointer exception despite using when and thenReturn

My ServiceImpl looks like this:
#Service
public class GuildUsersServiceImpl implements GuildUsersService {
#Autowired
private final GuildUsersRepository guildUsersRepository;
#Autowired
private GuildService guildService;
#Autowired
private UserService userService;
#Autowired
private GuildUserRoleTypeService guildUserRoleTypeService;
#Autowired
public GuildUsersServiceImpl(final GuildUsersRepository guildUsersRepository) {
this.guildUsersRepository = guildUsersRepository;
}
public GuildUsers create(GuildUsers guildUser) {
return this.guildUsersRepository.save(guildUser);
}
}
And my service test for create looks like this:
#RunWith(SpringRunner.class)
public class GuildUsersServiceTest {
#Mock private GuildUsersRepository guildUsersRepository;
#Mock private UserService userService;
#Mock private GuildService guildService;
#Mock private GuildUserRoleTypeService guildUserRoleTypeService;
#InjectMocks private GuildUsersServiceImpl guildUsersService;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
#Test
public void createTest() {
GuildUsers guildUsers = mockGuildUsers();
when(guildUsersRepository.save(guildUsers)).thenReturn(guildUsers);
GuildUsers dbGuildUsers = guildUsersService.create(guildUsers);
assertEquals(dbGuildUsers.getId(),guildUsers.getId());
}
}
I am using jUnit4. Despite using when(guildUsersRepository.save(guildUsers)).thenReturn(guildUsers); in the test, I run into a Null Pointer Exception with the following trace:
java.lang.NullPointerException: Cannot invoke "com.project.entities.domain.GuildUsers.getId()" because "dbGuildUsers" is null
I suspect something is wrong with how I have mocked the #Autowired classes. What needs to be done differently while mocking classes in the Test class ?
At the same time I want the services in the following test case to work as well :
#Test
public void createTest1() {
GuildUsers guildUsers = mockGuildUsers();
GuildUsersWithoutGuildIdRequestDTO guildUsersWithoutGuildIdRequestDTO = new GuildUsersWithoutGuildIdRequestDTO();
guildUsersWithoutGuildIdRequestDTO.setUserId(guildUsers.getUser().getId());
guildUsersWithoutGuildIdRequestDTO.setGuildUserRoleTypeId(guildUsers.getGuildUserRoleType().getId());
when(guildService.get(guildUsers.getGuild().getId())).thenReturn(Optional.of(guildUsers.getGuild()));
when(guildRepository.findById(any())).thenReturn(Optional.of(guildUsers.getGuild()));
when(userService.get(guildUsers.getUser().getId())).thenReturn(Optional.of(guildUsers.getUser()));
when(guildUsersRepository.findById(guildUsers.getId())).thenReturn(null);
when(guildUserRoleTypeService.get(guildUsers.getGuildUserRoleType().getId())).thenReturn(Optional.of(guildUsers.getGuildUserRoleType()));
when(guildUsersRepository.save(any())).thenReturn(guildUsers);
GuildUsersResponseDTO dbGuildUsersResponseDTO =
guildUsersService.create(guildUsers.getGuild().getId(),guildUsersWithoutGuildIdRequestDTO);
assertEquals(dbGuildUsersResponseDTO.getGuildUserRoleType(),guildUsers.getGuildUserRoleType());
}
I don't know why you did field injection as well as constructor injection at the same time. You should remove one of them first, for instance:
#Service
public class GuildUsersServiceImpl implements GuildUsersService {
//#Autowired - removed
private final GuildUsersRepository guildUsersRepository;
#Autowired
private GuildService guildService;
#Autowired
private UserService userService;
#Autowired
private GuildUserRoleTypeService guildUserRoleTypeService;
//#Autowired - removed
public GuildUsersServiceImpl(final GuildUsersRepository guildUsersRepository) {
this.guildUsersRepository = guildUsersRepository;
}
public GuildUsers create(GuildUsers guildUser) {
return this.guildUsersRepository.save(guildUser);
}
}
OR remove the constructor to add:
#Autowired
private final GuildUsersRepository guildUsersRepository;```
InjectMock documentation clearly says: "Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below."
In your case, it apparently chooses property injection, hence guildUserRoleTypeService remains uninitialised. Mixing property and constructor injection is not a good idea as pointed above.
To improve the general design and testability of your GuildUsersServiceImpl, I would suggest to stick with constructor injection. This way you can:
a) Declare all the fields as private final, so they will always be set once the object is created, avoiding race conditions and NPEs.
b) Initialise GuildUsersServiceImpl in the test using a proper constructor rather than InjectMocks.
Leaving this here for other people that also wasted an afternoon on trying to get a repository to return what they configured. What ended up being the thing I had to do is rather than annotating the repository with #Mock I had to use the #MockBean annotation for the Mockito/Spring combination to work properly.
As per javadoc of #MockBean:
When #MockBean is used on a field, as well as being registered in the application context, the mock will also be injected into the field.

How to create a test in Springt Boot that calls a service which includes constructor injection?

I'm trying to write a test in a Spring-Boot project. My problem is that I can't use my service that includes a constructor injection.
Depending on what I try I get errors like java.lang.IllegalStateException: Failed to load ApplicationContexts or NullPointerExceptions.
My first try was to change the constructor injection in my service to a field injection. But after reading this post I decided to change it back to the previous way.
Then I searched for examples but couldn't find something that was helpful.
Following are the relevant code snippets. If more code is needed I would provide it.
The service class with the constructor injection:
PlayerServiceImpl.java
#Service
public class PlayerServiceImpl implements PlayerService {
private PlayerRepository playerRepository;
private CompanyService companyService;
private CompanyResourceService companyResourceService;
#Autowired
public PlayerServiceImpl(PlayerRepository thePlayerRepository, CompanyService theCompanyService,
CompanyResourceService theCompanyResourceService) {
this.playerRepository = thePlayerRepository;
this.companyService = theCompanyService;
this.companyResourceService = theCompanyResourceService;
}
...
}
The test class im trying to create:
PlayerServiceImplIntegrationTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
public class PlayerServiceImplIntegrationTest {
#TestConfiguration
static class PlayerServiceImplTestContextConfiguration {
private PlayerRepository playerRepository;
private CompanyService companyService;
private CompanyResourceService companyResourceService;
#Bean
public PlayerService playerService() {
return new PlayerServiceImpl(playerRepository, companyService, companyResourceService);
}
}
#Autowired
private PlayerService playerService;
#MockBean
private PlayerRepository playerRepository;
#Before
public void setUp() {
Player max = new Player("MaxMustang", "test123", "MaxMustang",
"max.mustang#test.com", new Date(System.currentTimeMillis()), 1, 0,
new BigDecimal("0.00"), new BigDecimal("0.00"), 0, 0);
Mockito.when(playerRepository.findByUserName(max.getUserName()))
.thenReturn(max);
}
#Test
public void whenFindById_thenReturnPlayer() {
String userName = "MaxMustang";
Player found = playerService.findByUserName(userName);
assertThat(found.getUserName()).isEqualTo(userName);
}
}
In my test, I'm trying to create a player object and receive it. It's just my first test in Spring Boot. And my main goal was to just get the test running.
And the original test is from Baeldung from "5. Mocking with #MockBean". But while experimenting around, I added or changed a few things.
If I missed a post pointing at the same problem I would be glad to be informed about that.
Also, I would appreciate it if someone can tell me if the arguments in the constructor of my service are too much or still in an "ok" range.
You have to make the configuration bean primary and also use constructor injection on that method:
#TestConfiguration
static class PlayerServiceImplTestContextConfiguration {
#Bean
#Primary
public PlayerService playerService(PlayerRepository playerRepository,
CompanyService companyService, CompanyResourceService companyResourceService) {
return new PlayerServiceImpl(playerRepository, companyService, companyResourceService);
}
}
Without primary you will have two beans of same type floating around and you dont use #Qualifier here. Also you cannot #Autowire beans in a configuration class thats why you need to use constructor injection.

Spring MVC Controller Unit Testing : How do I set private instance boolean field?

I have a Spring MVC REST controller class that has a private instance boolean field injected via #Value ,
#Value("${...property_name..}")
private boolean isFileIndex;
Now to unit test this controller class, I need to inject this boolean.
How do I do that with MockMvc?
I can use reflection but MockMvc instance doesn't give me underlying controller instance to pass to Field.setBoolean() method.
Test class runs without mocking or injecting this dependency with value always being false. I need to set it to true to cover all paths.
Set up looks like below.
#RunWith(SpringRunner.class)
#WebMvcTest(value=Controller.class,secure=false)
public class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
....
}
You can use #TestPropertySource
#TestPropertySource(properties = {
"...property_name..=testValue",
})
#RunWith(SpringRunner.class)
#WebMvcTest(value=Controller.class,secure=false)
public class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
}
You can also load your test properties form a file
#TestPropertySource(locations = "classpath:test.properties")
EDIT: Some other possible alternative
#RunWith(SpringRunner.class)
#WebMvcTest(value=Controller.class,secure=false)
public class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private Controller controllerUnderTheTest;
#Test
public void test(){
ReflectionTestUtils.setField(controllerUnderTheTest, "isFileIndex", Boolean.TRUE);
//..
}
}
My preferred option would be to set it in the constructor and annotate the constructor parameter with the #Value. You could then pass in whatever you want in the test.
See this answer

#injectMocks does not work when using spring aop

I'm writing junit and I using #mock and #injectMock.But,I find #injectMocks doesn't work when bean in spring aop.code like this:
QuestionService.java:
#Component
public class QuestionService implements IQuestionService{
#Resource
private IUserService userService;
#Override
public User findUserById(long id) {
// TODO Auto-generated method stub
User user = userService.findUserById(id);
return user;
}
}
Test.java:
#Mock
IUserService mockuserService;
#InjectMocks
#Resource
QuestionService questionService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testfind() {
when(mockuserService.findUserById(1)).thenReturn(
new User(1, "name"));
User user = questionService.findUserById(1);
Assert.assertEquals(new User(1, "name"), user);
}
It works!
But,when I add userService in spring aop,It does not work!
For example, transaction aop.
How can I fix it?
I found an interesting behavior - once I used the AOP around any method in the class, the mocks stopped working; instead the 'real' component was initiated although there was no code for that matter.
I found that if you were to use #MockBean - everything works.
Why did you annotate QuestionService with #Resource in test class? Are you running with SpringJUnit4ClassRunner by loading bean configs? If not remove #Resource annotation and try, doesn't matter whether using AOP or not, it should work.
And add below snippet of code in #Before method of your test class as first line.
MockitoAnnotations.initMocks(this);
#InjectMocks : Mark a field on which injection should be performed.
MockitoAnnotations.initMocks(this): initializes fields annotated with Mockito annotations.

Request scope bean management in Unit Tests

There have been many questions regarding request scoped management in unit tests and mainly answer is to do not test the scope management, as its a Spring Framework task and it should take care that it works properly. So advice, for example, would be to replace the request scope with thread or prototype type scope in the XML configuration file.
For most of the tests its enough, there are no complaints about not registered "request" scope and tests are running fine. But I do have one case where it is not enough.
Consider following case:
#Component
#Scope("request")
public class MyService {
#Autowired
private MyComponent component;
public void doSomething(String param) {
component.doTheThing(param);
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"classpath:my-scope-tweaks.xml"})
public class MyServiceTest {
#Autowired
private MyService service;
#Autowired
private MyComponent component;
#Test
public void test1() {
service.doSomething("aaa");
assertEquals("AAA", component.getTheThing());
}
#Test
public void test1() {
service.doSomething("bbb");
assertEquals("BBB", component.getTheThing());
}
}
I want to test MyService, which is request-scoped. MyComponent is request scope as well.
Variant A
If I replace the request scope with SimpleThreadScope, then in by both tests I would receive the same instance of MyService and MyComponent, so for example test2() could receive bad results from MyComponent as it could internally contain some internal "trash" from previous test1()
Variant B
If I replace request scope with prototype scope - I would get the case where my test methods are receiving different instances of MyComponent as MyService does - so I cannot perform any assertions on them.
So what I would need is kind of test method-related request scope, where all request-scoped beans remain just during the test1() method and then gets destroyed, so within next test2() they would be created newly again.
Is it possible?
This may not be what you're looking for, but why are you using Spring to manage your test classes in the first place? That seems like overkill to me. For unit testing, you shouldn't need a DI container. Just mock the dependencies and focus on the encapsulated functionality of the Component or Service you're testing.
You won't be able to do that, though, while using field injection. You'll need to convert to constructor or method injection.
It is possible to receive a new context for every method in your unit test by annotating the method with #DirtiesContext. This allows you to manipulate beans in the application context for one unit test and not have it affect the others.
Documentation
Your example annotated with #DirtiesContext:
#Component
#Scope("request")
public class MyService {
#Autowired
private MyComponent component;
public void doSomething(String param) {
component.doTheThing(param);
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"classpath:my-scope-tweaks.xml"})
public class MyServiceTest {
#Autowired
private MyService service;
#Autowired
private MyComponent component;
#Test
#DirtiesContext
public void test1() {
service.doSomething("aaa");
assertEquals("AAA", component.getTheThing());
}
#Test
#DirtiesContext
public void test2() {
service.doSomething("bbb");
assertEquals("BBB", component.getTheThing());
}
}
If all of your test methods within your class require a new context you can also just annotate the class as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"classpath:my-scope-tweaks.xml"})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
public class MyServiceTest {
//Tests Here...
}

Categories

Resources