I am testing some service-layer methods on JUnit, but I got NPE error.
Here's my junit code
#Autowired
TeacherServiceImpl teacherServiceImpl;
#Test
public void test(){
String teacherName = "Bernice";
List<Teacher> teachers = new ArrayList<Teacher>();
teachers = teacherServiceImpl.findTeacherByName(teacherName);**(error here)**
}
Here's my service layer code
#Service
public class TeacherServiceImpl implements TeacherService {
#Autowired
private TeacherDao teacherDao;
public List<Teacher> findTeacherByName(String teacherName){
List<Teacher> teachers = new ArrayList<Teacher>();
teachers = teacherDao.findByNameCn(teacherName);
return teachers;
}
}
Any help will be appreciated!
It seems that TeacherServiceImpl is null. Try to use
#Autowired
TeacherService teacherService;
What's more, you should run your Test Case within Spring, that means you should start Spring Framework by adding #Runwith and #SpringBootTest annotation to your test class if you were using Spring Boot.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
public void TeacherServiceTest {
...
}
You should write your tests properly.
Integration testing for the web layer and Unit testing for the service layer.
You are trying to write integration test using #Autowired but I assume that you did not configure it as an integration test. NPE means that teacherServiceImpl reference has no object refer to. #Autowired works only when you have Spring Context with beans that could be injected.
You should not write an integration test for your service, an only Unit test is suitable here (using JUnit + Mockito).
Read please what is Unit testing in general:
What is unit testing and how do you do it?
Unit testing example:
https://howtodoinjava.com/spring-boot2/spring-boot-mockito-junit-example/
Integration testing example:
https://dzone.com/articles/integration-testing-in-spring-boot-1
But I recommend reading more info before coding next time ;)
Man, which class are you testing? if you want to test the TeacherServiceImpl, then you shouldnt mock this, but use constructor (you are testing the mock atm). Second, in your TeacherServiceImpl.class you have a dependency. Unless you mock this dependency and stub the method u use (findByNameCn), you ll be getting NPE
So, in your test, use the constructor for the class you re testing and mock the dependences using Mockito. Dont forget to use the proper annotation for Runner or ExtendWith
I'm assuming that you did not annotate the test class with #SpringBootTest. For this reason you don't have the application context and therefore no injection.
Please read up on testing with Spring :-)
Related
I am getting ready to take my Spring Certification v5.0 and there appears to be a question: Do you use Spring in a unit test? Link to Exam Guide questions.
From Spring reference guide I know this:
The POJOs that make up your application should be testable in JUnit or
TestNG tests, with objects simply instantiated using the new operator,
without Spring or any other container.
From my study as well I can tell that we only are using Spring when testing controllers (like below), repositories or when creating integration tests and maybe some other cases. We would need the TestContext in these cases which is part of org.springframework.* package.
#RunWith(SpringRunner.class)
#WebMvcTest(HelloWorldController.class)
So, is the correct answer of this question: No we do not use Spring? or that, Yes we do need it. Because we obviously use it in some cases.
The first paragraph you mentioned is the answer to your question, you don't need Spring to write unit tests for the classes you wrote, even when they're Spring beans.
The other cases you mentioned aren't really unit tests. When testing a repository using SpringRunner and a mock database, you're no longer writing a unit test, but an integration test. The same applies to writing tests for your controller using MockMvc. In both cases you're testing the integration between the Spring framework (Spring MVC or Spring Data) with your code (and a database).
However, you can write unit tests for your controller, but in that case, you would rather do something like this:
#Controller
public class HelloWorldController {
#RequestMapping("/hello")
public ModelAndView getHello() {
return new ModelAndView("hello", "title", "hello world");
}
}
public class HelloWorldControllerTest {
private HelloWorldController controller;
#Before
public void setUp() {
controller = new HelloWorldController();
}
#Test
public void getHelloShouldUseHelloView() {
assertThat(controller.getHello().getViewName()).isEqualTo("hello");
}
#Test
public void getHelloShouldAddATitleModel() {
assertThat(controller.getHello().getModel()).containsEntry("title", "Hello world");
}
}
Spring is a framework. A framework calls you, so by definition, using spring with your controller is an integration point and does not belong in a unit test.
I have successfully been testing my controllers' behaviour without involving spring whatsoever.
I have also tested the controller's annotation successfully by asking spring to mock the controller and invoking the mockMvc.
I'm doing some integration tests, on a Spring Boot application.
Usually the integration tests that I was used to develop, was regarding the application domain, without any external service involved.
Since this time I need to make an integration test on a service which uses both a database and an external service called by an SDK, I've tried doing something like the following:
#RunWith(PowerMockRunner::class)
#SpringBootTest
#PowerMockRunnerDelegate(SpringRunner::class)
#PrepareForTest(McpProductService::class)
class MyServiceIntegration {
#Mock
private ExternalService externalService;
#Autowired
#InjectMocks
private MyServiceImpl myService;
#Test
public void thisTestShouldWork() {
...
}
}
What is confusing me is: how should I declare myService attribute? Usually when I use Mockito + PowerMock in my Unit Tests, I usually test the implementation, not the whole Service Interface + Spring Injection. But I can't use #Autowired if I'm using just it's implementation, not the Interface.
Is there any best practice for this issue that I'm facing?
Disclaimer: I'm assuming that what you are after is an end-to-end test of a service interface, backed by multiple classes. I assume (and hope) that you don't have a single class handling both database and webservice integration.
I don't see the need to use PowerMock here, it is usually something one would use for testing legacy code with a lot of static stuff. If you are using Spring boot, your code should be of a quality that makes PowerMock unnecessary.
When writing an end-to-end test, the principles are the same as a per-class unit test, only with a larger scope:
With a unit test, you create an instance of the class under test, and mock all its external dependencies (other classes)
With an end-to-end test, you create an "instance" of your module under test, and mock its external dependencies.
So, here you should find a mechanism to mock the parts of your code that communicates with external sources, like web service clients, database classes (if you don't use an in-memory db for your test (you should)). This will typically be a Spring config that is almost identical to the one used in production, but with said parts mocked out. Then, you just #Inject the parts you need to communicate with in order to complete the test.
Assuming that you use component scan and annotations for all beans, you could mock the endpoint-classes and use profiles:
This code is based on memory only, might not work on copy-paste, but hopefully you could use the concepts..
#Profile("test")
#Configuration
public class TestConfiguration {
#Bean
#Primary
public SomeWebserviceClient someWebserviceClient() {
return mock(SomeWebserviceClient.class);
}
}
Production code:
#Service
public class SomeClass {
#Inject
private SomeWebserviceClient client;
}
Then in the test:
#RunWith(PowerMockRunner::class)
#SpringBootTest
#ActiveProfiles("test")
public class SomeTest {
#Inject
private SomeClass someClass;
#Inject
private SomeWebserviceClient client; //<< will inject mock
}
Mock will also be injected into SomeClass
I have a question about the usage of SpringJUnit4ClassRunner. For pure Junits or Unit Test cases should we use Spring based annotations such as #Autowired along with SpringJUnit4ClassRunner or should we use only the MockitoJUnitRunner instead with the #RunWith annotation at the top of the Test class?
I mean replacing
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-applicationContext.xml" })
with just
#RunWith(MockitoJUnitRunner.class)
at the top of the class. It works for me.
In Junits we normally do not make any external calls such as calls to DB or call to some other web service. We have to mock these external calls using #Mock annotations on this service objects. And then create a real object of the class that we are testing and that depends on these mocks. We can then use #InjectMocks on the real object so that it will be injected with the mocked objects.
Example Service-A->Calls->Service-B->Calls->Service-C
While testing A we should mock Service B & while testing Service-B we should mock Service-C.
Some code Snippet
#RunWith(MockitoJUnitRunner.class)
public class TestServiceA {
#Mock
B mockObj;
#InjectMocks
A realObj;
#Test
public void testServiceA() {
.....
.....
}
}
So, I feel for Unit test cases we need not rely on Spring container to provide us the instance of the class we are testing.
Please give your suggestions.
Using SpringJUnit4ClassRunner.class instead of MockitoJUnitRunner.class
If you try to simply unit test a class without the dependencies, like you describe, there is no need for the SpringJUnit4ClassRunner. This runner is able to generate a complete Spring context with (mock) objects you can define in your (test) application context configuration. With this mechanism the SpringJUnit4ClassRunner is much slower than the regular MockitoJUnitRunner.
The SpringJUnit4ClassRunner is very powerful for integration test purposes.
I default start with the MockitoJUnitRunner and if I reach the limits of this runner, for instance because I need to mock constructors, static methods or private variables, I switch to PowerMockJUnitRunner. This is for me a last resort as it normally tells the code is ‘bad’ and not written to be tested. Other runners are normally not necessary for isolated unit tests.
Building on Sven's answer, Let us say that you needed to test an assembly of classes while mocking out bits that go to the database, or call an external service, you would look to run the test with SpringJUnit4ClassRunner.
If you were trying to test a Single Java Class as a Unit, mocking out both the integration bits and local collaborators, then running the test with MockitoJUnitRunner is sufficient and faster as well.
Given the following members of my test class
#Mock
private Gateway gateway;
#Autowired
#InjectMocks
private TransactionManager transactionManager;
#BeforeClass
public void before() {
MockitoAnnotations.initMocks(this);
}
The TransactionManager uses the gateway internally and it is wired up with #Autowired. When I run the tests in this class, they pass. However when I run tests in a separate class that I am expecting to use the concrete implementation of Gateway, they are using the mocked Gateway.
You have to check out the Mockito's subproject for TestNG. You can see an example of usage here in my Mockito Cookbook repo - https://github.com/marcingrzejszczak/mockito-cookbook/blob/master/chapter01/src/test/java/com/blogspot/toomuchcoding/book/chapter1/_3_MockitoAnnotationsTestNg/assertj/MeanTaxFactorCalculatorTestNgTest.java.
To use the listener you have to copy the contents of the https://github.com/mockito/mockito/tree/master/subprojects/testng/src/main/java/org/mockito/testng folder to your project since mockito-testng is not yet released.
I had the same Problem. You can use #DirtiesContext on the test class.
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html
In this case you don't need any extra packages or code. The context will be reinitialized after your test.
In my project I'm having trouble doing unit testing. One issue is that just doing an integration test is much faster to write and also tests that the components actually work together. Unit testing novel "algorithms" or so seems much easier. Unit Testing service classes it just feels wrong and useless.
I'm using mockito to mock spring data repository (and hence DB access). The thing is if i tell the mocked repository to return entity A on method call getById it will obviously return that and the service will return it too. Yes, the service does some extra stuff, but very minor things, like load lazy collections (from hibernate). Obviously I don't have any lazy collections (proxies) in a unit test.
Example:
#Test
public void testGetById() {
System.out.println("getById");
TestCompound expResult = new TestCompound(id, "Test Compound", "9999-99-9", null, null, null);
TestCompoundRepository mockedRepository = mock(TestCompoundRepository.class);
when(mockedRepository.findOne(id)).thenReturn(expResult);
ReflectionTestUtils.setField(testCompoundService, "testCompoundRepository",
mockedRepository, TestCompoundRepository.class);
TestCompound result = testCompoundService.getById(id);
assertEquals(expResult, result);
}
hooray, the rest succeeds. What a surprise! Not really no.
Can some one explain to me what I'm doing wrong? Or else what the point of such a test is? I mean I tell to return expResult and then it is returned. Wow. What a surprise! Feels like I'm testing if mockito works and not my Service.
EDIT:
The only benefit I see if some were stupid error happens like leaving an unwanted line there that sets return value to null or something similar stupid. Such cases would be caught by the unit test. Still the "reward-effort" ratio seems bad?
Question might be a bit old but I will put an answer in case someone stumbles across.
I'm using Mockito and JUnit.
AccountRepository is a plain spring data repository extending JPARepository.
Account is a plain JPA entity.
To test your services and mock Spring Data repositories, you need something like below.
package foo.bar.service.impl;
import foo.bar.data.entity.Account;
import foo.bar.data.repository.AccountRepository;
import foo.bar.service.AccountService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class AccountServiceImplTest {
#Mock
private static AccountRepository accountRepository;
#InjectMocks
private static AccountService accountService = new AccountServiceImpl();
private Account account;
#Test
public void testFindAccount() {
Integer accountId = new Integer(1);
account = new Account();
account.setId(accountId);
account.setName("Account name");
account.setCode("Accont code");
account.setDescription("Account description");
Mockito.when(accountRepository.findOne(accountId)).thenReturn(account);
Account retrivedAccount = accountService.findAccount(accountId);
Assert.assertEquals(account, retrivedAccount);
}
}
One of the reasons I like testing my Spring Data repositories is to test that I have defined my JPA mappings correctly. I do not use a mocking framework for these tests, I use the Spring Test framework which actually bootstraps the container allowing me to autowire the actual repository into the Junit test so that I may execute tests against it.
I agree with your thoughts that mocking the repository is pretty useless. Since your using Spring I would suggest leveraging the Spring Test framework to perform real tests against your repositories, which can be executed against an embedded database such as H2 in a more unit test based fashion or your actual database implementation such as Oracle or MySql, to conduct more of an integration test. (Execute these against a copy of a development database) These tests will reveal fallacies in your JPA mappings and other items such as improper cascades setup in the database.
Here is an example of one of my tests on GitHub. Notice how the framework actually autowires the repository into the test. The repository also contains an example of how to configure the Spring Test framework, which I have also demonstrated in this blog post.
In conclusion, I do not believe you will receive any of the benefits of testing a repository that I have discussed from using a mock of the repository.
One additional note I wanted to add, is that mocks are not really intended for use in the actual class under test. Their use is for providing required dependencies to a class under test.
You can use this library: https://github.com/agileapes/spring-data-mock
This will mock your repository for you, while allowing you to implement custom functionality for any method as well as your native query methods.
You exactly right. It is clear unit test. And it will never fail (so, it is useless) I think you need at integration test to test real JPA repository with real database (H2 in memory for example) (as I always do).
And it is better to test your services (theirs interfaces). If after some time you will change your storage (to Mongo for example) - you will be able to use your service tests to ensure all works as before.
After some time you will be suprised how many DB\JPA-related problems (constraints, optimistic locks, lazy-loading, duplicate id, some hibernate issues and so on) you find.
Also, try to develop via tests - not just write test after implementation. Instead before creation of new method in service - create test for it, implement service method and only after just recheck it in real application. At least it is much faster to start test than a server.
So, do not create tests to have a lot of them. Find how they may help you.
Usage of mocks for repositories is not good idea. Test how your services work together with Hibernate\JPA\Database. Most part of problems is located beetwen layers.
You can mock the repository and inject it to the service, this is the way; but, if you just instantiate the service with #Mock of repositories, it would be better, if you define the repositories as private final fields in the service and use a constructor of all repositories. That way, if you add another repository to the service, the test will fail and you have to change it, which is the purpose.
Imagine this service:
class OrderService {
private final UserRepository userRepos;
public OrderService(UserRepository userRepos) {
this.userRepos = userRepos;
}
...
}
And this test:
class OrderServiceTests {
#Mock
private UserRepository userRepos;
private OrderService service;
private OrderServiceTests() {
this.service = new OrderService(this.userRepos);
}
}
Now, if we add another dependency to the service:
class OrderService {
private final UserRepository userRepos;
private final AddressRepository addRepos;
public OrderService(UserRepository userRepos, AddressRepository addRepos) {
this.userRepos = userRepos;
this.addRepos = addRepos;
...
}
The previous test will fail because the constructor has changed. If you use #InjectMocks this will not happen; the injection happens behind the curtain and we are not clear what happens; this may not be desirable.
Another thing is, I don't agree that integration test will cover all the cases that unit tests will cover; it may but not always the case. Even the controller can be unit-tested with mocks; after all the tests are meant to cover all the code we have written, so they must be fine-grained; imagine when we follow TTD and we only complete the controller and services level: how we proceed without controller unit testing?
Assuming that we have the below Service class
#Service
public class EmployeeServiceImpl implements EmployeeService {
#Autowired
private EmployeeRepository employeeRepository;
#Override
public Employee getEmployeeByName(String name) {
return employeeRepository.findByName(name);
}
}
Test class:
#RunWith(SpringRunner.class)
public class EmployeeServiceImplIntegrationTest {
#TestConfiguration
static class EmployeeServiceImplTestContextConfiguration {
#Bean
public EmployeeService employeeService() {
return new EmployeeServiceImpl();
}
}
#Autowired
private EmployeeService employeeService;
#MockBean
private EmployeeRepository employeeRepository;
// write test cases here
}
To check the Service class, we need to have an instance of Service class created and available as a #Bean so that we can #Autowire it in our test class. This configuration is achieved by using the #TestConfiguration annotation.
During component scanning, we might find components or configurations created only for specific tests accidentally get picked up everywhere. To help prevent that, Spring Boot provides #TestConfiguration annotation that can be used on classes in src/test/java to indicate that they should not be picked up by scanning.
Another interesting thing here is the use of #MockBean. It creates a Mock for the EmployeeRepository which can be used to bypass the call to the actual EmployeeRepository:
#Before
public void setUp() {
Employee alex = new Employee("alex");
Mockito.when(employeeRepository.findByName(alex.getName()))
.thenReturn(alex);
}
After the setup, we can easily test our service like:
#Test
public void whenValidName_thenEmployeeShouldBeFound() {
String name = "alex";
Employee found = employeeService.getEmployeeByName(name);
assertThat(found.getName())isEqualTo(name);
}
For more in depth knowledge check:
https://www.baeldung.com/spring-boot-testing