This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 4 years ago.
This is my main code:
#Service
public class MainService {
public String mainMethod() {
SomeService someService = new SomeService("required");
// do Sth.
}
}
#Service
#RequiredArgsConstructor
public class SomeService {
#Autowired
private SomeOtherService someOtherService;
#Notnull
private final String requiredField;
// ...
}
#Service
public class SomeOtherService {
// just a bunch of public methods that might as well be static
}
And this is the test setup:
#RunWith(SpringRunner.class)
public class MainServiceTest {
#InjectMocks
private MainService mainService;
#Test
public void givenSth_whenSth_doSth() {
// test mainService.mainMethod();
}
}
Can you please tell me why someOtherService inside SomeService is null?
I figured out when yo use Spring's injection, you should not use manual instantiation (new ...) at the same time; so maybe new SomeService("required") is the problem? But then how do I inject field variables into SomeService if not by constructor call? I don't want to use the Builder because requiredField is supposed to be NotNull.
You can follow Adam Weidner's tutorial using MockBean for Spring Bean and extending AbstractTestNGSpringContextTest for TestNG with Spring
#TestExecutionListeners(MockitoTestExecutionListener.class)
#SpringBootTest
public class WidgetServiceIntegrationTest extends AbstractTestNGSpringContextTest {
#MockBean private SprockService sprockService;
#MockBean private SpringService springService;
#Autowired WidgetService widgetService;
#Test
public void testSomethingOnWidgetService() {
when(sprockService.makeASprock()).thenReturn(...);
For integration testing consider instantiating bean using context configuration like below
#RunWith(SpringRunner.class)
#ContextConfiguration("mockito-config.xml")
For a detailed explanation, please refer
https://blog.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/
For unit testing, Please consider using #Mock annotation provided by Mockito.
In this specific example, We could mock SomeOtherService as shown below
#RunWith(SpringRunner.class)
public class MainServiceTest {
#InjectMocks
private MainService mainService;
#Mock
private SomeOtherService someOtherService
#Test
public void givenSth_whenSth_doSth() {
// test mainService.mainMethod();
}
}
For a detailed explanation please refer the following post
http://www.vogella.com/tutorials/Mockito/article.html
http://static.javadoc.io/org.mockito/mockito-core/2.23.0/org/mockito/MockitoAnnotations.html
Related
I use Spring Boot 5 and JUnit in my project. I create a unit test to test the service.
Here is the service that I am testing:
#Service
#RequiredArgsConstructor
#Slf4j
public class BuilderServiceImpl implements BuilderService{
#Autowired
public AutoMapper autoMapper;
private final BuilderRepository builderRepository;
private final AdminUserRepository adminUserRepository;
#Override
public BuilderDto getByEmail(String email){
}
#Override
public List<BuilderMinDto> getAll() {}
#Override
public List<BuilderMinDto> getAll(int page, int size) {}
#Override
public SaveBuilderResponse create(Builder builder){
var str = autoMapper.getDummyText();
Builder savedBuilder = builderRepository.save(builder);
return new SaveBuilderResponse(savedBuilder);
}
}
And here is the test class that tests the service above:
#SpringBootTest
#RequiredArgsConstructor
#Slf4j
class BuilderServiceImplTest {
#Mock
private BuilderRepository builderRepository;
#Mock
private AdminUserRepository adminUserRepository;
private AutoCloseable autoCloseable;
private BuilderService underTest;
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
#AfterEach
void tearDown () throws Exception{
autoCloseable.close();
}
#Test
void getByEmail(){}
#Test
#Disabled
void getAll() { }
#Test
#Disabled
void testGetAll() {}
#Test
void create() {
//given
Builder builder = new Builder();
builder.setName("John Johnson");
builder.setCompanyName("Builders Test");
builder.setEmail("test#builders.com");
//when
underTest.create(builder);
//then
ArgumentCaptor<Builder> builderArgumentCaptor = ArgumentCaptor.forClass(Builder.class);
verify(builderRepository)
.save(builderArgumentCaptor.capture());
Builder captureBuilder = builderArgumentCaptor.getValue();
assertThat(captureBuilder).isEqualTo(builder);
}
}
When I start to run the test class the create method in BuilderServiceImpl fired and on this row:
var str = autoMapper.getDummyText();
I get NullPointerException(autoMapper instance is null).
Here is the definition of AutoMapper class:
#Component
#Slf4j
#RequiredArgsConstructor
public class AutoMapper {
public String getDummyText(){
return "Hello From AutoMapper.";
}
}
As you can see I use #Component annotation to register the AutoMapper class to the IoC container and Autowired annotation to inject it into autoMapper property in BuilderServiceImpl class.
Why autoMapper instance is null? How can I make autoMapper to be initialized?
In order to make #Autowire work you have to use the instance of BuilderServiceImpl (object under test) created by spring itself.
When you create the object like this (by yourself, manually):
#BeforeEach
void setUp(){
....
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
Spring doesn't know anything about this object, hence Autowiring won't work
Another thing that might be useful:
You've used #Mock for BuilderRepository and AdminUserRepository.
This are plain mockito annotation, and if you're using an integration/system test that runs the spring under the hood, probably this is not what you want:
Surely, it will create a mock, but it won't put it onto an application context, and won't substitute the beans of these classes that might have been created by spring.
So if this is what you want to achieve, you should use #MockBean instead.
This annotation belongs to Spring Testing framework rather than a plain mockito annotation.
All-in-all you might end up with something like this:
#SpringBootTest
class MyTest
{
#MockBean
BuilderRepository builderRepo;
#MockBean
AdminUserRepository adminUserRepo;
#Autowired // spring will inject your mock repository implementations
// automatically
BuilderServiceImpl underTest;
#Test
void mytest() {
...
}
}
Add #Autowire annotation Annotations on below fields. Error due your not initialized below object In BuilderServiceImpl
#Autowire
private final BuilderRepository builderRepository;
#Autowire
private final AdminUserRepository adminUserRepository;
Why are you creating BuildService manually? If you do this, set AutoMapper manualy too.
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
underTest.setAutoMapper(new AutoMapper());
}
You are not using di.
This question already has answers here:
Injecting #Autowired private field during testing
(6 answers)
Closed 3 years ago.
I have two classes, Parent and Child, and want to unit test some methods in the Child class using Mockito.
public abstract class Parent {
#Resource Service service;
}
#Service // spring service
public class Child extends Parent {
private AnotherService anotherService;
#Autowired
Child(AnotherService anotherService) {
this.anotherService = anotherService;
}
public boolean someMethod() {
}
}
My test class looks like below:
#RunWith(MockitoJUnitRunner.class)
public class ChildTest {
#Mock
Service service;
#Mock
AnotherService anotherService;
#InejctMocks
Child classToTest;
#Test
public void testSomething() {
assertTrue(classToTest.someMethod());
}
}
And the issue I am facing is that anotherService is being mocked properly but service is null. Can someone please tell me how to successfully mock both of the services (dependencies) in my test class?
inside the test, class add this and try
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
If you use Spring:
#SpringBooTest
#RunWith(SpringRunner.class)
#MockBean(Resource.class)
class TestClass{
#Autowire
private Resource resource;
#Autowire
private Service service;
#Test
public void test(){
Mockito.when(resource).getSomething(Mockito.any(),Mockito.any()).thenThrow(new RuntimeException(""));
service.doSomething();
}
}
You can use Mockito:
// Or use MockitoAnnotations.initMocks(testClass); in before method instead rule
#RunWith(MockitoJUnitRunner.class)
class TestClass{
#Mock
Resource resource;
#InjectMocks
Service service;
#Test
public void test(){
Mockito.when(resource).getSomething(Mockito.any(),Mockito.any()).thenThrow(new RuntimeException(""));
service.doSomething();
}
}
This is my PersonSeviceImpl class. Here before saving any person I want to run some validators which implements PersonValidator. And also I autowired PersonStatusChangeValidator to use in a different method.
public class PersonServiceImpl implements PersonService {
#Autowired
PersonDao personDao;
#Autowired
List<PersonStatusChangeValidator> statusChangeValidators;
#Autowired
List<PersonValidator> personValidators;
#Override
public Person save(Person person) {
for(PersonValidator validator: personValidators){
validator.validate(person);
}
personDao.save(person);
return person;
}
}
Here I am writing test to verify whether validators are called or not.
#RunWith(PowerMockRunner.class)
public class PersonServiceImplTest {
#Mock
private PersonDao personDao;
#Mock
private PersonValidator personValidator;
#Mock
private PersonStatusChangeValidator statusChangeValidator;
#Spy
private List<PersonStatusChangeValidator> statusChangeValidators = new ArrayList<>();
#Spy
private List<PersonValidator> personValidators = new ArrayList<>();
#InjectMocks
private PersonServiceImpl personService;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
personValidators.add(personValidator);
statusChangeValidators.add(statusChangeValidator);
}
#Test
public void testCreatePerson() throws Exception {
personService.save(new Person());
verify(personValidator, times(1)).validate(any(Person.class));
}
}
The problem is personValidators, statusChangeValidators are null by the time I'm running test. And when there is a single #Spy annotation in the test, it is working fine. Need some help to know where I'm doing wrong.
Your test doesn't show any mocking of statics or finals so I'm wondering why you are using #RunWith(PowerMockRunner.class)?
I have taken your code and successfully run it with just one change: replacing #RunWith(PowerMockRunner.class) with #RunWith(MockitoJUnitRunner.class).
So, unless there is some other aspect of your tests which require PowerMock then I'd suggest running your test case with MockitoJUnitRunner. If there is some aspect of your tests which require PowerMock then I'd suggest injecting the statusChangeValidators and personValidators instances with explicit constructor injection (i.e. what #JB Nizet suggested in his comment above) so that you do not have to rely on #InjectMocks.
FWIW, there's an open issue against PowerMock for this inability to apply #InjectMocks for fields with the #Spy annotation.
I have a service say MainService and it has few managers which are initialised with #Autowired, and it is using some external service which are also #Autowired.
My purpose is to create unit test cases so that I can access the inmemory DB with managers, and want to mock the external service.
Now problem which I am facing is if I use #Autowired in my unit test and use #Mock for external services, then it doesn't use mock methods, Instead it uses the actual implementation. If I do #InjectMocks then it doesn't pick the data from repo as it doesn't find the respective dependencies for managers, and if I use #Autowired and #InjectMocks together it still not being able to use the Mocks.
Something like this
#Service
public class MainService extends AbstractService
{
#Autowired
Manager1 manager1;
#Autowired
Manager2 manager2;
#Autowired
Manager3 manager3;
#Trace(dispatcher = true)
public void mainMethod(int data)
{
int data1 = manager1.getData(int xyz);\\ getting data from DAO
int data2 = manager1.getData(int xyz);\\ getting data from DAO
int data3 = manager1.getData(int xyz);\\ getting data from
\\External Service
}
}
Now the test case I am writing is
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
public class TestClass {
#InjectMocks
#Autowired
MainService service;
#Autowired
RepoForManager1 repoManager1;
#Autowired
RepoForManager2 repoManager2;
#Mock
Manager3 manager3;
#Before
public void initTest()
{
MockitoAnnotations.initMocks(this);
int dataFirst=1;
int dataSecond =2;
int dataThird=3;
int dataForMethod=4;
repoManager1.save(dataFirst);
repoManager2.save(dataSecond);
}
#Test
public void testMethod()
{
Mockito.when(manager3.getData(Mockito.anyInt())).thenReturn(dataThird);
service.mainMethod(dataForMethod);
}
}
This is a replication of the actual service, when I debug the test I found that the mock is not being used, its using actual implementation, and when I removed #Autowired from MainService then it only execute the mocked method.
Instead of using Field Injection(using #Autowired on class variables) use Constructor Injection. This way you can initialize your MainService class with some mocks and some real Implementations. Something like this
#Service
public class MainService extends AbstractService
{
private final Manager1 manager1;
private final Manager2 manager2;
private final SomeExternalService externalService;
#Autowired
public MainService(Manager1 manager1, Manager2 manager2, SomeExternalService externalService)
this.manager1= manager1;
this.manager2= manager2;
this.externalService = externalService;
}
........................
}
From your test class don't Autowire MainService. Just Autowire Manager1 and Manager2 and Create Mock for SomeExternalService(and initialize it). And then create instance of MainService using constructor.
public class TestClass {
MainService service;
#Autowired
RepoForManager1 repoManager1;
#Autowired
RepoForManager2 repoManager2;
#Mock
SomeExternalService externalService;
#Before
public void setUp(){
service = new MainService(repoManager1, repoManager2, externalService);
}
}
If you want to use Mockito, you need Annotate your TestClass with #RunWith(MockitoJUnitRunner.class) instead of #RunWith(SpringRunner.class).
Then for the #AutoWired in the TestClass for repoManager1, repoManger2. Annotate them with #Mock instead of #Autowired, as you want to mock them with Mockito.
I have not used the SpringRunner myself, but from a quick read, I can see that it is used mostly for Integration Test, where you want to load the SpringContext..etc.
Also #ActiveProfiles("test") is mostly used for Integration Test, where you want to load the spring context, with the 'test' profile properties.
I'm using an autowired constructor in a service that when instantiated in the test class causes the #Value annotations to return null. Autowiring the dependencies directly solves the problem but the project follows the convention of using constructor based autowiring. My understanding is that instantiating the service in the test class is not creating it from the Spring IoC container which causes #Value to return null. Is there a way to create the service from the IoC container using constructor based autowiring without having to directly access the application context?
Example Service:
#Component
public class UpdateService {
#Value("${update.success.table}")
private String successTable;
#Value("${update.failed.table}")
private String failedTable;
private UserService userService
#Autowired
public UpdateService(UserService userService) {
this.userService = userService;
}
}
Example Test Service:
#RunWith(SpringJUnite4ClassRunner.class)
#SpringApplicationConfiguration(classes = {TestApplication.class})
#WebAppConfiguration
public class UpdateServiceTest {
private UpdateService updateService;
#Mock
private UserService mockUserService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
updateService = new UpdateService(mockUserService);
}
}
To make #Value work updateService should be inside of spring context.
The best practice for spring framework integration tests is to include application context in test context and autowiring test source in test:
...
public class UpdateServiceTest {
#Autowired
private UpdateService updateService;
...
Mock userService
Option with changing userService to protected and considering that test and source classes are in same package.
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
updateService.userService = mockUserService;
}
Option with reflection with Whitebox:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Whitebox.setInternalState(updateService, 'userService', mockUserService);
}
The #Value is filled by a property placeholder configurer which is a post processor in the spring context. As your UpdateService is not part of the context it is not processed.
Your setup looks a little like a unclear mixture of unit and integration test. For a unit tests you will not need a spring context at all . Simply make the #Value annotated members package protected and set them or use ReflectionTestUtils.setField() (both shown):
public class UpdateServiceTest {
#InjectMocks
private UpdateService updateService;
#Mock
private UserService mockUserService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(updateService, "successTable", "my_success");
updateService.failedTable = "my_failures";
}
}
For an integration test all wiring should be done by spring.
For this I added a inner config class providing the mock user service (the #Primary is only for the case you have any other user service in your context) and the mock is stored in a static member here to have simple access to the mock from the tests afterwards.
#RunWith(SpringJUnite4ClassRunner.class)
#SpringApplicationConfiguration(classes = {TestApplication.class, UpdateServiceTest.TestAddOn.class})
#WebAppConfiguration
public class UpdateServiceTest {
#Autowired
private UpdateService updateService;
private static UserService mockUserService;
static class TestAddOn {
#Bean
#Primary
UserService updateService() {
mockUserService = Mockito.mock(UserService.class);
return mockUserService;
}
}
}