I m new to JUnit and Mockitio. When I run the below code, I get,
java.lang.NullPointerException: Cannot invoke "org.springframework.http.ResponseEntity.getStatusCodeValue()" because the return value of "com.learnit.testing.MyController.getUser(java.lang.Long)" is null
#Controller
#RequestMapping("/api")
public class MyController {
#Autowired
private MyService service;
#GetMapping("/")
#ResponseBody
public ResponseEntity<Object> getUser(Long id) throws Exception {
return service.myResponse(id);
}
}
#Service
public class MyService {
#Autowired
private MyRepository repository;
public ResponseEntity<Object> myResponse(Long id) throws Exception{
MyData data=repository.findById(id).orElse(null);
if(data!=null)
return new ResponseEntity<>(HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Testing
#ExtendWith(MockitoExtension.class)
public class MyTest {
#InjectMocks
private MyController controller;
#Mock
private MyRepository repo;
#Mock
private MyService service;
#Test
public void checkService() throws Exception {
when(repo.findById((long)1)).thenReturn(null);
assertEquals(controller.getUser((long)1).getStatusCodeValue(), 500);
}
}
There are several issues:
You did not stub the behaviour of the MyService#myResponse method. You need to mock the MyService instance as this is the direct dependency of the MyController instance. Therefore, the MyRepository don't needs to be mocked in this test case (unless you want to stub its methods for some reason, but I don't it in the current code). I would recommend you to replace the following:
when(repo.findById((long)1)).thenReturn(null);
with stubbing behaviour of the MyService mock instance:
when(service.myResponse(1L)).thenReturn(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
When you do this, the MyRepository test suite mock instance can be removed, as it is not required anymore.
Check your imports, based on the usage of #ExtendWith annotation I assume you are using JUnit 5 here. Make sure that you actually use other classes from org.junit.jupiter.api(for example #Test and Assertions). By default, the JUnit does not support backward compatibility with the JUnit 4. Still, I you want to keep the compatibility you should include the junit-vintage-engine artifact in your test runtime path.
P.S You need to think which class you actually want to test, that determines which instances should be mocked.
IMHO you need to add a #RunWith(some.mockito.TestSuiteIDontRememberName.class) annotated to the test class. The #Mock annotation requires a test suite that searches for the annotation and gathers the information to inject the values there.
If I were you, I'd first try to initialize the mocked instances in a #Before method and see if the test runs... then, I'd read the Mockito (sorry, but I normally use EasyMock) library documentation, and check how is the #Mock annotation used (I can tell you you have chances for this to be the problem)
And there's a problem in your test code... you are telling the mocked instance to return null when calling method findById(), so most probably if that was to happen, you'll get a NPE in the main code, when trying to use the .orelse() method. Probably you don't know what to return, or you need to return an also mocked object instance... but as such, it is impossible to repair your code without implementing something completely different to your intention.
Anyway, you have eliminated enough things in your code to make it untestable, and in this process you have not included how do you call the method to be tested at all.
Please, read the page How to create a Minimal, Reproducible Example and edit your question to include testable code, showing that NPE, so we can, at least, see how and where it is occuring.
Related
I have a very bad bean implementation
#Component
public class Repository{
public List<String> people= new ArrayList<>();
And I also have a test where I replace the repository with a mock.
But when I try to access the "people" field in the test via the repository mock i get NullPointerException
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class MainControllerTest{
#Autowired
private MockMvc mockMvc;
#MockBean
private Repository repository;
#Test
public void someTest(){
repository.people // null -> NullPointerException
}
}
Why it happens? Is the bean initialized initially or not? What's the correct solution with such a bad implementation?
Bean initialized as it suppose to be with #MockBean.
It produces just a stub for you. All nested stuff of the Repository class is ignored.
In your case you have a completely initialized mock. But of course it's nested fields are null. Becase it is a mock and nested fields will not be initialized. Mock assumes that you just need kind of wrapper of that particular class to mock some behavior on that.
What you need to do is to change #MockBean to the #SpyBean.
In that case your class fields would be initialized as it defined in your Repository class. So, you will have a real Repository object but wrapped with Mockito Spy.
And it will allow you to perform all testing manipulations on that object that needed.
I have a service class that I need to unit test. The service has a upload method which in turn calls other services(autowired beans) that updates the database. I need to mock some of these services and some to execute as it is.
#Service
public class UploadServiceImpl implements UploadService{
#Autowired
private ServiceA serviceA;
#Autowired
private ServiceB serviceB;
public void upload(){
serviceA.execute();
serviceB.execute():
//code...
}
In the above example I need to mock ServiceA, but i would like ServiceB to run as is and perform it's function.
My Junit test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes=Swagger2SpringBoot.class)
public class UploadServiceTest {
#Mock
private ServiceA serviceA;
#InjectMocks
private UploadServiceImpl uploadService;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testUpload(){
uploadService.upload();
}
When I execute this I get NPE at serviceB.execute(); in UploadServiceImpl.
What could be the problem?
Note: I am not specifying the behavior of the mocked object because I don't really care and also default behavior of mocked objects are to do nothing.
Thanks!
Usually when unit testing you want to mock all external dependencies of a class. That way the unit test can remain independent and focused on the class under test.
Nevertheless, if you want to mix Spring autowiring with Mockito mocks, an easy solution is to annotate with both #InjectMocks and #Autowired:
#InjectMocks
#Autowired
private UploadServiceImpl uploadService;
The net effect of this is that first Spring will autowire the bean, then Mockito will immediately overwrite the mocked dependencies with the available mocks.
The issue you are facing is due to the use of #InjectMocks annotation.#InjectMocks marks a field on which injection should be performed. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection – in this order. If any of the given injection strategy fail, then Mockito won’t report failure.
So in your case when trying to inject mocks only one mock bean is present and the other bean ServiceA is not getting injected.To solve this issue :
You can try not using #InjectMocks at all instead pass a mock object for the method that you want to mock while pass rest of the autowired objects into the constructor.Example :
Here to test i am passing one mock object and one autowired object.
#RunWith(MockitoJUnitRunner.class)
public class SampleTestServiceImplTest {
#Mock
private SampleClient sampleClient;
#Autowired
private BackendService backendService ;
private BackendServiceImpl backendServiceimpl;
#Before
void setUp() {
backendServiceimpl = new BackendServiceImpl(sampleClient, backendService);
}
Or another way you can make this work is by using #Autowired annotation along with the #InjectMocks.#Autowired #InjectMocks are used together and what it will do is inject the mocked class and Autowired annotation adds any other dependency which the class might have.
Answer referred from : https://medium.com/#vatsalsinghal/autowired-and-injectmocks-in-tandem-a424517fdd29
In my opinion, we are writing unit test cases and we should not initialize the spring context in order to test a piece of code.
So,
I used Mockito to mock the Autowired beans in my main target test class and injected those mock beans in my main test class Object
maybe sounds confusing, see the following example 💥
Dependencies I used
testImplementation("org.mockito:mockito-core:2.28.2")
testImplementation("org.mockito:mockito-inline:2.13.0")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
testImplementation("org.mockito:mockito-junit-jupiter:4.0.0")
My main class is Maths and Calculator bean is autowired
class Maths{
#Autowired Calculator cal;
.........
.........
public void randomAddMethod(){
cal.addTwoNumbers(1,2); // will return 3;
}
}
Test class
#ExtendWith(MockitoExtension.class)
class MathsTest{
#Mock(answer = Answers.RETURNS_DEEP_STUBS) Calculator cal;
#InjectMocks Maths maths = new Maths();
#Test testMethodToCheckCalObjectIsNotNull(){
maths.randomAddMethod();
}
}
Now cal will not be null in Maths class and will work as expected
Add
#Mock
private ServiceB serviceB;
to create injectable mock of missing service just like you did with service A.
In my case, beside using combination of #InjectMocks and #Autowired, I also had to provide setter for the mocked object in the tested class (setter for ServiceA in UploadServiceImpl in the original example). Without that the real method of ServiceA was called.
Another way is to define an autowired constructor so that you can test the services properly.
#Service
public class UploadServiceImpl implements UploadService{
private ServiceA serviceA;
private ServiceB serviceB;
#Autowired
public UploadServiceImpl(ServiceA serviceA, ServiceB serviceB) {
this.serviceA = serviceA;
this.serviceB = serviceB;
}
public void upload(){
serviceA.execute();
serviceB.execute():
//code...
}
I couldn't make it work without using ReflectionTestUtils. Setting the constructor is one option if it's viable for you.
I have a RepositoryRestController that exposes resources for some persistent entities.
I have a method on my controller that takes a PersistentEntityResourceAssembler to help me generate the resources automatically.
#RepositoryRestController
#ExposesResourceFor(Customer.class)
#RequestMapping("/api/customers")
public class CustomerController {
#Autowired
private CustomerService service;
#RequestMapping(method = GET, value="current")
public ResponseEntity getCurrent(Principal principal Long id, PersistentEntityResourceAssembler assembler) {
return ResponseEntity.ok(assembler.toResource(service.getForPrincipal(principal)));
}
}
(Contrived example, but it saves going into too much detail about irrelevant details of my use-case)
I'd like to write a test for my controller (my real use-case is actually worth testing), and am planning on making use of #WebMvcTest.
So I have the following test class:
#RunWith(SpringRunner.class)
#WebMvcTest(CustomerController.class)
#AutoConfigureMockMvc(secure=false)
public class CustomerControllerTest {
#Autowired
private MockMvc client;
#MockBean
private CustomerService service;
#Test
public void testSomething() {
// test stuff in here
}
#Configuration
#Import(CustomerController.class)
static class Config {
}
}
But I get an exception saying java.lang.NoSuchMethodException: org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler.<init>()
Presumably something is not being configured correctly here because I'm missing the entire data layer. Is there some way of mocking out the PersistentEntityResourceAssembler? Or another approach I could use here?
I ended up for now with:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
The downsite of it is that the test would start the full Spring application context (but without the server).
I ended up doing a slightly hacky solution here:
I removed PersistentEntityResourceAssembler from the controller method.
I added an #Autowired RepositoryEntityLinks to the controller, on which I call linkToSingleResource to create the links as needed.
I added an #MockBean RepositoryEntityLinks to my test class, and configured the mocking to return something sensible:
given(repositoryEntityLinks.linkToSingleResource(any(Identifiable.class)))
.willAnswer(invocation -> {
final Identifiable identifiable = (Identifiable) invocation.getArguments()[0];
return new Link("/data/entity/" + identifiable.getId().toString());
});
It's far from ideal - I'd love to know if there's a way of getting just enough of the data layer up that I can depend on PersistentEntityResourceAssembler.
//Update
After viewing helpful comments, I realize the problem should then be, how to unit test method using values read from properties by #Value .
//
I am working on this issue for days, I am writing unit test for a serviceClass.The serviceClass is like below :
import ...
#Component
public class ServiceClass implements ServiceInterface {
#Value("${data.layer.url}")
private String dataLayerUrl;
#Autowired
private RestTemplate restTemplate
public void dummy(){
restTemplate.postForObject(dataLayerUrl + "/" + ... , ...);
}
}
And CONFIG_DIR is already defined in application configuration file.
I have a SomeConfig class defining beans as below. (...src/main/java/com.app/configuration/SomeConfig)
#Configuration
#ComponentScan(basePackages = {"..."})
#PropertySource(value = "file:${CONFIG_DIR}/app.properties")
public class SomeConfig{
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
...
return restTemplate;
}
}
My test class is as below:
Import ...
#Profile("test")
public class ServiceClassTest extends AbstractTest {
#Value("${data.layer.url}")
private String dataLayerUrl;
#InjectMocks
private ServiceClass ServiceClass;
#Mock
RestTemplate restTemplate;
#Before
public void initializeMockito() {
MockitoAnnotations.initMocks(this);
}
#Test
public void dummyTest(){
when(restTemplate.postForObject(dataLayerUrl + "/" + ..., ...)).thenReturn(...);
serviceClass.dummy();
assertEquals(...);
verify(restTemplate).postForObject(...);
}
}
And then my AbstractTest as below :
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
#SpringApplicationConfiguration(classes = Application.class)
#ContextConfiguration(classes = {TestConfiguration.class})
#ComponentScan(basePackages = ...)
public abstract class AbstractTest {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
}
And I also have .../src/test/resources/application-test.properties defined as below
#Datalayer properties
data.layer.url=http://camel-dev-01.xxx.com:5001
This is the same as defined in application.properties(which locates outside of project in CONFIG_DIR.
The logic of testing is just to make sure when you call dummy method of serviceClass, the postForObject method of restTemplate is called exactly once.
But when doing it this way, I am facing with 2 problems.
when I run test class in debug mode, I found
in ServiceClassTest. dataLayerUrl = "$data.layer.url"
in ServiceClass. dataLayerUrl = null
I researched around and be able to solve problem one by following this link
https://gist.github.com/danlangford/3418696
But this is not an ideal way to do this, since by default spring should be able to read properties from application-test.properties.
And I never figured out what caused the second issue and how to solve it.
I think this would be a common issue when writing unit test on class which read properties from .properties file using $Value annotation. Any comments or suggestions would be very much appreciated.
The key point hear as said M. Deinum is that you use a mix of Spring bean and Mock Object that in this case aren't Spring bean and for this reason can't benefit of the feature of Spring Container such as the injection of the properties.
In particular you should use the spring test abstraction as a "integration test" istruments. With this words I intended that you should use this abstraction, for test the correct configuration, behavior and so on fo your bean in the spring contex. However if you use Stub or mock object you actually exit, of a smal part probably, by the management of spring and the your test don't make sense. Using stub or mock the your test become a Unit test in sense that it will be a test the your bean and functionality in isolation infact you have mock or stub the dependency of your object.
I hope that this reflection could be help you
I am glad to know there is no way to read values from properties by #Value inside a mock obj.
But still my problem is that I want to unit test my dummy method in ServiceClass. Put it another way, as long as I could unit test this method, I don't care whether #Value works or not.
Here is my solution of test method
#Profile("test")
public class ServiceClassTest extends AbstractTest {
#Value("${data.layer.url}")
private String dataLayerUrl;
#InjectMocks
private ServiceClass ServiceClass;
#Mock
RestTemplate restTemplate;
#Before
public void initializeMockito() {
MockitoAnnotations.initMocks(this);
}
#Test
public void dummyTest(){
when(restTemplate.postForObject(anyString() , eq(), eq() )).thenReturn(...);
serviceClass.dummy();
assertEquals(...);
verify(restTemplate).postForObject(anyString(), eq(), eq());
}
By using anyString, I don't rely on what value is read from properties, since I only want to test whether dummy method call restTemplate's postForObject method properly.
You need to add PropertySourcesPlaceholderConfigurer to your test configuration in order to populate properties annotated with #Value annotation. Spring Boot adds it to configuration, but since your test is running without Spring Boot you have to declare it. For more details seehere .
in place of #InjectMocks you can write #Autowired and I thing you can use both annotation like that
Case 1
#InjectMocks
private ServiceClass ServiceClass;
case 2
#Autowired
#InjectMocks
private ServiceClass ServiceClass;
I have same issue but after discussion my senior I have find above like solutions
I have so controller
#Controller
public class MyController {
#Autowire
MyClass myClass;
//myClass doesn't have setter and getter
....
#RequestMapping("/path")
public String underTest(){
myClass.makeSomething();
return "html.jsp"
}
I want make mock test using Mockito and mock myClass.
In test class I want get myClass so:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/BeanConfig.xml");
myClass = context.getBean("myClass ", MyClass .class);
But I need autowire this bean to Controller for testing controller's method(I think test code should not affect to normal code).
There are exist way to make it without writing of set method?
I want to check that myClass.makeSomething() invokes once in method underTest.
As long as your test for MyController resides in the same package as MyController itself (as it's usually done - same packages in different source folders), you can simply assign it:
MyController controller = new MyController();
controller.myClass = mockMyClass;
That's the reason not to put #Inject/#Autowired on private fields.
I'm not sure I agree with you that test code should not affect normal code. I think an entirely valid reason to refactor / rewrite production code is to make it more testable (this is probably achieved by making it more modular, which is generally a good thing anyway).
This is precisely why annotations like
#VisibleForTesting
exist. Then you can create a package-local setter for MyClass, add the above annotation (for information to other programmers and possibly code inspection tools) and set the field in your test (which should reside in the same package).
Alternatively, since you are using Mockito, you could simply annotate the MyController instance with #InjectMocks, eg
#Test
public class MyControllerTest {
#Mock
private MyClass mockMyClass;
#InjectMocks
private MyController myController;
#BeforeMethod
public void before() {
MockitoAnnotations.initMocks(this);
}
// do tests...
}
Note that #InjectMocks does not depend on any annotations on the target field (i.e. #Autowired, #Resource, #Inject etc). It just works. (Presumably you will still need those annotations for Spring injection, so don't remove them! The point is you can also use it for fields that aren't annotated).
Note also that, depending on which version of Mockito you are using, you may need to instantiate the MyController in the before() method before calling MockitoAnnotations.initMocks()
Try testing the controller directly with context.getBean(). MyClass will be autowired into it.
I agree with #axtavt's answer, however if you absolutely want to go your way with injecting the mock in an integration test, you can do this:
define a overriding bean configuration file, say bean-test-config.xml, with content along these lines:
<import resource="classpath:spring/BeanConfig.xml"/>
<bean name="myClass" factory-method="mock" class="org.mockito.Mockito">
<constructor-arg value="MyClass"></constructor-arg>
</bean>
This should correctly inject in a mock in your controller. You will have to get hold of this mock in your test and inject in any behavior that you are expecting from this mock though.