Testing service layer with Mockito/Spring with constructor injection - java

I use constructor injection so my test should look like this now.
import org.junit.Assert;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import com.user.model.jpa.User;
import com.user.repository.UserRepository;
import com.user.service.UserService;
import com.job.service.JobService;
#RunWith(MockitoJUnitRunner.class)
public class UserServiceImplTest {
#Mock
private UserRepository userRepository;
#Mock
private JobService jobService;
#Mock
private User testObject;
private UserService userService;
#Before
public void setUp() {
userService = new UserServiceImpl(userRepository, jobService);
}
#Test
public void testAdd() {
//given
testObject = new Mock(User.class); //commented line
testObject.setName("name");
//when
userService.add(user);
//then
Assert.assertEquals("name",task.getName());
}
}
Is this possible to mock like I did in commented line?
(obviously object need more fields, but for test case I need only 1 field)
I want to avoid creating new object manually with all fields, because it has too much fields and contaings of two other classes
(id, name, surname, Address address, Job job etc.)
This is my add method in serviceImpl:
#Override
public void add(User user) {
user.setName(StringUtils.trimToNull(user.getName()));
user.setSurname(StringUtils.trimToNull(user.getSurname()));
user.setJob(jobService.getCorrectJob());
user.setCreateDate(LocalDateTime.now());
userRepository.save(user);
}
UPDATE:
With information PLOG gave me in an answer I created test like this:
//given
testObject.setId(999);
testObject.setName("name");
//when
userService.add(testObject);
//then
assertEquals("name", userService.getById(999).getName());
But it still caugh: java.lang.NullPointerException
UPDATE2:
I resolved null pointer exception by removing method userService.getById(999).getName() in assertEquals.
The correct and working test in my example is:
//given
testObject = new User();
testObject.setName("name");
//when
userService.add(testObject);
//then
assertEquals("name", testObject.getName());

You can do this. You don't need to instantiate the mock like your commented code though. The mock already exists because of the #Mock annotation along with the fact you are using the MockitoJUnitRunner.
To test your add method just use the mock User object as the method argument:
userService.add(testObject);
Although to be honest there is no real reason to mock the User. Just instatiate a user and only set the fields you are interested in. As far as I can see from your code having a null name or surname wouldn't break anything.

Related

NPE while running test in Spring application (JUnit 5, Mockito 3, Spring JPA repositories)

I'm running a basic Spring App with Mockito 3.1.0 and and Junit 5.5.2. I have a Service call that includes two Spring Data JPA repositories. These are passed into the constructor for DI (along with two others that are immaterial – I'm including them in case they could also, potentially, cause errors.) I see no issues with the service when the app runs.
When I run my test, I get a NPE for myService. Stepping through the stack trace, hasn't really shown me anything that relates to the error. I have also tried (following this Article: https://www.baeldung.com/mockito-junit-5-extension) updating my test class to look like this:
#ExtendWith(MockitoExtension.class)
#RunWith(JUnitPlatform.class) // This dependency doesn't seem to exist
public class MyServiceTest {
// ...
#BeforeEach
// not the JUnit4 #Before annotation.
// Interestingly, this gives me NPEs for the repositories, not the service.
public void setup(){
// ...
}
}
to no avail. What I suspect is happening is that something about my setup isn't properly wired up – either as dependencies or syntax for DI.
How do I debug this? What am I missing? Thanks in advance!
Service:
import org.springframework.stereotype.Service;
#Service
public class MyService {
private final Repository1 repository1;
private final Repository2 repository2;
private final Repository3 repository3;
private final Repository4 repository4;
public MyService(Repository1 repository1,
Repository2 repository2,
Repository3 repository3,
Repository4 repository4) {
this.repository1 = repository1;
this.repository2 = repository2;
this.repository3 = repository3;
this.repository4 = repository4;
}
public Boolean computeValue(String someInput) {
// does computations with repository1, repository2.
}
}
Test:
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#Mock
private Repository1 repository1;
#Mock
private Repository2 repository2;
#Mock
private Repository3 repository3;
#Mock
private Repository4 repository4;
#InjectMocks
private MyService myService;
#Before
public void setup {
when(repository1.findAll()).thenReturn(new ArrayList<>());
when(repository1.findAllByInput(anyString())).thenReturn(new ArrayList<>());
// Yes; I'm aware that this could also be a call to
// MockitoAnnotations.initMocks(this). I've tried it:
// it doesn't work. Also, I've intentionally not taken this
// approach due to reasons:
// - https://stackoverflow.com/questions/10806345/runwithmockitojunitrunner-class-vs-mockitoannotations-initmocksthis
}
#Test
void callMyService() {
assertTrue(myService.computeValue("123"));
}
}
Sample Repository:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
// This is just an example, but this pattern is repeated against all
// repositories in the project.
#Repository
public interface Repository1 extends JpaRepository<Repository1, String> {
}
Edit:
I forgot to mention that I have other files in this project that are using exactly these annotations (#RunWith(MockitoJUnitRunner.class), #Mock, #InjectMocks, #Before) that are not failing.
I updated the files with the relevant imports, and added an example of RepositoryN.
I update the MyService class to better reflect the parameters.
For anybody else who encounters this in the future, we were able to fix this problem by changing one of the imports from:
import org.junit.jupiter.api.Test;
to
import org.junit.Test;
Edit:
This had to do with differing versions of JUnit. There's a good long-form explanation as to why here.
Seems like your object myService, is not instantiated. I would suggest not use #InjectMocks and directly create your object as your repositories are already instantiated.
MyService myService = new MyService(..., ..., ...)
I suppose you have to annotate your test class with #ExtendWith(SpringExtension.class) and not with MockitoExtension.class
More info here Junit 5 with Spring Boot: When to use #ExtendWith Spring or Mockito?

Getting NotAMockException on the below Test Case

I have tried to run the below test and am facing NotAMock Exception and not sure how to resolve it. I have been trying to read the concept that methods of class under test cannot be mocked but I am unable to come clear on the subject. If someone could explain me the Why on my own example, I am hopeful of understanding it better.
I tried various ways of changing #RunWith runners for Unit or Integration test setup or using #Spy instead of #Mock or not have #Autowired etc but either was facing dao Null Pointer or Not a Mock Exception variably.
Am I supposed to Use another class and inject the Listener in that class and mock the listener to achieve the functionality of being able to mock the methods and capture the arguments dynamically. Will this work because it is no more the class under test and therefore the methods could be mocked? If so, how is this realized. If not, what is the right way. My sense is moving the listener to another class will only extend my current set of issues of not being able to mock but does not resolve it. However, I am not sure what is the right outcome.
#Component
public class FileEventListener implements ApplicationListener<FileEvent> {
#Autowired private FetchFileDetailsDAO fileDao;//Dao is annotated with #Transactional
#Override
public void onApplicationEvent(FileEvent event) {
fileDao.getDetailsForFile(event.fileName())
}
}
-----------------------------------------------------------------------------------------
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
#SpringBootTest(classes = TestApp.class)
#RunWith(SpringRunner.class)
public class TestClass {
#Captor private ArgumentCaptor<Object> captor;
#Mock #Autowired private FetchFileDetailsDAO dao;
#InjectMocks #Autowired private FileEventListener listener;
#Before
public void setup() throws IOException {
MockitoAnnotations.initMocks(this);
}
#Test
#Transactional
#Rollback(true)
public void test() throws Exception {
FileEvent ev = new FileEvent();
...
listener.onApplicationEvent(ev);
verify(dao, times(1)).getDetailsForFile((String)captor.capture())
}
You are mixing things up here. There is an important difference between #Mock and #MockBean.
You use the first annotation if you want to write a unit test without any Spring Context support (speak #SpringBootTest, #DataJpaTest, etc.). For such tests, you can use #Mock and #InjectMocks.
As you are writing an integration test (you are starting the whole context with #SpringBootTest), you work with managed Spring beans inside your test. Hence you are not writing a unit test anymore.
If you want to replace a Spring bean with a mocked version of it inside your Spring Test Context, you have to use #MockBean:
#SpringBootTest(classes = TestApp.class)
#RunWith(SpringRunner.class)
#RunWith(MockitoJUnitRunner.class) // will do the Captor initialization for you
public class TestClass {
#Captor
private ArgumentCaptor<Object> captor;
#MockBean
private FetchFileDetailsDAO dao;
#Autowired
private FileEventListener listener;
#Test
#Transactional
#Rollback(true)
public void test() throws Exception {
FileEvent ev = new FileEvent();
// ...
listener.onApplicationEvent(ev);
verify(dao, times(1)).getDetailsForFile((String)captor.capture())
}
Starting the whole context however for this test is IMHO overkill. You are better off writing a good old unit test with just JUnit and Mockito.
In addition to this, also I would rethink what benefit your current tests adds to your project as it is literally duplicating the business logic. Maybe there is more code that is not present here.
You can find a more detailed summary for the difference between #Mock and #MockBean in this article.
I think the problem is the following line
#Mock #Autowired private FetchFileDetailsDAOImpl dao;
Try #Mock private FetchFileDetailsDAOImpl dao; instead

Problem with testing service that utilises data from mongodb repository

I am trying to test a service that has a mongodb repository. I dont know how to write tests that utilise it though and when I try to create entries from the testing class I keep on getting null pointer exception when I call a method from the mongodb repository. My repository class is called
TagPreferencesRepository
and the exception is thrown when I call
tagPreferencesRepository.deleteAllByTag(tag);
I have declared TagPreferencesRepository using #Mock:
#Mock TagPreferencesRepository tagPreferencesRepository;
and the method throwing the exception is called from inside a method in the testing class, that is annotated with #Before
Methods in the testing class annotated with #Before:
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
and:
#Before
public TagPreferences setUp() {
tagPreferencesRepository.deleteAllByTag(tagEnum);
...
}
Your mock is not being correctly initialized.
It could be that you are not injecting the mock to the instance of what you want to test. Do you have a #InjectMock annotation?
Also, make sure that you are using jUnit for your annotations. I once had a similar experience because I was using org.junit.jupiter.api for some of the annotations instead of org.junit.
Example skeleton:
import org.junit.Before;
import org.junit.Test
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
public class TestSomeService()
{
#InjectMock
SomeService someService;
#Mock
TagPreferencesRepository tagPreferencesRepository;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void SomeTest(){
TagEnum tag = SomeTag;
// if deleteAllByTag is void
doNothing().when(tagPreferencesRepository).deleteAllByTag(tag);
someResult = someService.someMethod(tag);
// assert something
}
}

How to mock returned object from inner method on some other class in java

I read at least 20 posts but still couldn't find the answer. So, posting this question. May be it is answered in some other post, which I couldn't find.
class OuterService {
InnerService innerService;
#Autowired
public void setInnerService(InnerService innerService){
this.innerService = innerService;
}
public void method() {
List<C> listOfC = new ArrayList<C>();
C c = new C();
c.setUserProfiles(someObject);
c = innerService.invokeMethod(String str1,Map map,
String str2, Object obj1, String str3, String str4,
C c, String str5);
c.setCreatedDate(some String value); // Here c comes null while executing jUnits.
listOfC.add(c);
}
}
Here is my Test class:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import com.pogo.service.DeviceRegistrationService;
#SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT")
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
class ClassOuterServiceTest {
#InjectMocks
OuterService outerService;
#Mock
InnerService innerService;
#Mock C c;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
outerService.setInnerService(innerService);
}
#Test
public void methodTest() {
when(innerService.invokeMethod(Mockito.anyString(),
Mockito.any(Map.class), Mockito.anyString(),Mockito.anyString(),
Mockito.any(PersonSessionToken.class), Mockito.anyString(),Mockito.anyString(), Mockito.anyString(),
Mockito.any(RequestHeader.class),Mockito.any(C.class),
Mockito.anyString() )).thenReturn(c);
doNothing().when(c).invokeCMethod();
outerService.method();
}
}
But I get null inside object c in OuterService.java. Also if I use Matchers.any() or Matchers.anyString() in invokeMethod() then , it shows Matchers exception.
What is the appropriate solution?
You don't need to create the object for OuterService use #InjectMocks annotation and when you use method stubbing use mock objects only. In your program you are creating object for c. Instead of creating object just use #Mock annotation.
When you using Mockito.any() mention the class inside parenthesis. Mockito.any(C.class) like this.
Don't use PowerMockRunner unless you are testing static or final classes.
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
public class ClassOuterServiceTestC {
#Mock
public InnerService innerService;
#InjectMocks
public OuterService outerService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void methodTest() {
C obj = mock(C.class);
when(innerService.invokeMethod(anyString(), anyMap(), anyString(), any(), anyString(), anyString(), any(TestC.class), anyString()))
.thenReturn(obj);
doNothing().when(obj).setUserProfiles(any(Object.class));
doNothing().when(obj).setCreatedDate(anyString());
outerService.method();
}
}

How to get away with NPE for #Inject field while unit testing?

Im writing unit tests for the below code, Im getting NPE even though Im mocking the fields, How can I resolve that. Those fields are present with #Inject annotation
#Component
interface A {
void run();
}
class B {
#Inject
A a;
void someMethod() {
a.run();
}}
class C{
#Inject
B b;
void anotherMethod() {
b.someMethod();
}
}
class CTest {
#Mock
B b;
// remains null when invoked in the actual class though its mocked instance is
// present here
#Mock
A a;
//// remains null when invoked in the actual class though its mocked instance
//// is present here
#InjectMocks
C c;
#Before
public void initialize() {
MockitoAnnotations.initMocks(this);
}
#Test
public void test() {
c.anotherMethod();
}
}
So how can I get the mocked value in the actual class where the field is injected with #Inject using Mockito?
My guess is that you should annotate your CTest class with #RunWith(MockitoJUnitRunner.class) and remove your before-method as it will become unecessary (#RunWith will do the trick of injecting mocks).
UPDATE
Actually I ran your code in my IDE. And everything is just fine, no NPE. Probably you have to check that your imports are correct. Here are mine to compare with yours:
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import javax.inject.Inject;
import org.springframework.stereotype.Component;
Also, please, pay attention that you declared Class C with upper case letter (should be class C) in your question, thus this very code won't compile.

Categories

Resources