Getting NotAMockException on the below Test Case - java

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

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?

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
}
}

Create #MockBean with qualifier by annotating class?

In my Spring Boot test I'm using 2 mock beans with different qualifiers:
#RunWith(SpringRunner.class)
#SpringBootTest
class HohoTest {
#MockBean #Qualifier("haha") IHaha ahaha;
#MockBean #Qualifier("hoho") IHaha ohoho;
}
Since I'm not using these beans explicitly, I would rather move them away from the class body, as the #MockBean annotation is now repeatable:
#RunWith(SpringRunner.class)
#SpringBootTest
#MockBean(IHaha.class)
#MockBean(IHaha.class)
class HohoTest {}
However, I need to pass in a qualifier as well, since they have the same type. Any idea on how I can achieve that?
Because using annotation #Qualifier means choose bean by name, so you can set up a name for a mock with code like this:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {JsonMapperConfig.class})
public class IntegrationFlowTest {
#MockBean(name = "s3MessageRepository")
private S3Repository s3MessageRepository;
// etc
If it is okay to move the mock definition completely out of the test class, you could also create the mocks in a separate #Configuration class:
#Configuration
public class MockConfiguration
{
#Bean #Qualifier("haha")
public IHaha ahaha() {
return Mockito.mock(IHaha.class);
}
#Bean #Qualifier("hoho")
public IHaha ohoho() {
return Mockito.mock(IHaha.class);
}
}
When declaring #MockBean at the class level, there is currently no support for providing a qualifier.
If you would like to have such support, I suggest you request it in the Spring Boot issue tracker.
Otherwise, you will need to continue declaring #MockBean on fields alongside #Qualifier.
I had a similar requirement of injecting mocked service beans with #Order annotation. I also needed to verify the invocation count of service functions. Below is my implementation. It might help someone.
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceNameTest {
#Autowired private ServiceName serviceName;
// Important: Used to reset interaction count of our static
// bean objects before every test.
#Before
public void reset_mockito_interactions() {
Mockito.clearInvocations(MockServicesConfig.bean1);
Mockito.clearInvocations(MockServicesConfig.bean2);
}
#Configuration
public static class MockServicesConfig {
public static InterfaceName bean1;
public static InterfaceName bean2;
#Bean
#Order(1)
public InterfaceName bean1() {
bean1 = Mockito.mock(InterfaceName.class);
// Common when() stubbing
return bean1;
}
#Bean
#Order(2)
public InterfaceName vmpAdapter() {
bean2 = Mockito.mock(InterfaceName.class);
// Common when() stubbing
return bean2;
}
}
#Test
public void test_functionName_mock_invocation1() {
// Arrange --> Act --> Assert
// nullify other functions custom when() stub.
// updating this functions custom when() stub.
verify(MockServicesConfig.bean1, times(1)).functionName("");
}
#Test
public void test_functionName_mock_invocation2() {
// Arrange --> Act --> Assert
// nullify other functions custom when() stub.
// updating this functions custom when() stub.
verify(MockServicesConfig.bean1, times(1)).functionName("");
}
}
This should now work
#SpringBootTest(
classes = Some.class
)
#MockBean(name = BEAN_NAME, classes = TheBeanClass.class)
#MockBean(name = BEAN_NAME_2, classes = TheBeanClass.class)
class SomeTest {
private final Some some;
#Autowired
SomeTest(Some some) {
this.some = some;
}
}
Please note, if you need to use any of the mocked beans, you will have to put the #Qualifier in the constructor, for example
private final TheBeanClass theBeanclass;
private final Some some;
#Autowired
SomeTest(Some some, #Qualifier(BEAN_NAME) TheBeanClass theBeanClass) {
this.some = some;
this.theBeanClass = theBeanClass;
}

How to mock public API (Third Party API) to generate spring restdocs

I am able to generate restdocs for rest services which is created by me, but unable to generate docs for services which i am consuming.
Is there any way to test and generate docs for third party API.
Sample code which i am using to generate to docs for local services.
#RunWith(SpringRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = RestdocApplication.class)
public class CountryDocumentation {
private static final Logger logger =
LoggerFactory.getLogger(CountryDocumentation.class);
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Rule
public final JUnitRestDocumentation restDocumentation = new
JUnitRestDocumentation("target/generated-snippets");
#Mock
private CountryService countryService;
#Mock
private RestTemplate restTemplate;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).apply(documentationConfiguration(this.restDocumentation)
.uris().withHost("X.X.X.X").withPort(9090).and().operationPreprocessors()
.withResponseDefaults(prettyPrint())
.withRequestDefaults(prettyPrint())).defaultRequest(get("/")).build();
}
#Test
public void getCountryDefinition() throws Exception {
this.mockMvc.perform(get("/"))
.andExpect(status().is(200))
.andDo(document("{ClassName}/{methodName}"));
}
}
You've said in a comment that you want to mock the actual call to the remote service. I would argue that makes the documentation pointless. If your tests that generate the documentation are calling a mocked service, you're documenting the mock not the service. If you want the benefits of REST Docs' test-driven approach to documentation generation, your tests need to call the service that is being documented. If the service is only remotely accessible then you'll need to make HTTP calls to document it.
You can use Spring REST Docs with REST Assured or WebTestClient to document any service that's accessible via HTTP. Here's an example with REST Assured that documents part of Stack Exchange's API:
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.restdocs.JUnitRestDocumentation;
import static io.restassured.RestAssured.given;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.documentationConfiguration;
public class RestAssuredExampleTests {
#Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
private RequestSpecification documentationSpec;
#Before
public void setUp() {
this.documentationSpec = new RequestSpecBuilder()
.addFilter(documentationConfiguration(this.restDocumentation))
.setBaseUri("https://api.stackexchange.com/2.2").build();
}
#Test
public void answers() throws Exception {
given(this.documentationSpec).accept(ContentType.JSON).filter(document("answers"))
.when().get("answers?order=desc&sort=activity&site=stackoverflow").then()
.assertThat().statusCode(200);
}
}
There are many products for mocking/virtualizing services. Including SoapUI and Parasoft Virtualize.

Testing service layer with Mockito/Spring with constructor injection

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.

Categories

Resources