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.
Related
I have the following test class:
#SpringBootTest
public class ChoreControllerTest
{
#Autowired
private ChoreController controller;
#Test
public void throwOnMissingChore()
{
assertThrows(ChoreNotFoundException.class, () -> this.controller.getChore(0L));
}
}
It takes about 5 seconds for Spring Boot to start up so the test can run. I want to reduce this time, but if I just remove the #SpringBootTest annotaton, I just get a NPE.
Is there a way to make this controller test more lightweight, or am I stuck with the startup time? I'm especially worried about what will happen to my test times if I ever want to test more than one controller....
The #SpringBootTest annotations create a Spring Context for you therefore it takes a while to start up. This annotation is mostly used for integration tests where a Spring context is required. Here are a few tips for optimizing integration tests.
If you remove the annotation the ChoreController cannot be autowired (no context available) which results in a NullpointerException.
Depending on your needs you can just use a Mocking library like Mockito to inject mocks e.g. services that your controller class needs and run the test without the #SpringBootTest.
You might want to take a look at this article for setting up those mocks properly.
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 :-)
I am developing a REST API with Spring Boot.The problem it's that I have one interface and two implementations and I want to test only with the mock implementation.
Interface CRMService
#Service
CRMServiceImpl
#Service
CRMServiceMock
Implementations: the first one is the real integration with the backend and the second is a mock for testing purposes, what's the best approach? Integration test or test based on the active profile ? If I need to autowire a service based on profile what's the best practice?
While I'm sure there's exceptions, generally it shouldn't be integration or unit tests (often involves mocks), but both; see testing pyramid concept.
Integration tests: just use the real service. If it calls out to other live services, then consider injecting the URLs as Spring Boot properties which point to mock servers in the test environment (Node.js or something easy and quick).
Unit tests: Consider using a test-framework like Mockito. Using this you can write your tests with mocks approximately like so:
private CRMServiceImpl mockService = mock(CRMServiceImpl.class);
#Test
public void someTest() {
when(mockService.someMethod(any(String.class), eq(5))).thenReturn("Hello from mock object.")
}
The above example roughly translates to "when some class invokes 'someMethod(String, int)' on your service, return the String specified".
This way allows you to still use mocks where necessary, but avoids having to maintain entire mock implementation profiles and avoids the problem of what to auto-wire.
Finally, if you need a full separate implementation, consider not auto-wiring services! Instead, use #Bean annotations in your configuration class and inject it via constructors into the classes that need it. Something like so:
#Configuration
public class ApplicationConfiguration {
#Value{$"service.crm.inmem"} // Injected property
private boolean inMem;
#Bean
CRMService getCRMService() {
if (inMem) {
return new CRMServiceMock();
}
return new CRMServiceImpl();
}
#Bean
OtherService getOtherService() {
// Inject CRMService interface into constructor instead of auto-wiring in OtherService.class
return new OtherService(getCRMService());
}
}
An example of when you could use ^^ would be if you wanted to switch between an in-memory store, and a real database-connection layer.
Personally I'd suggest doing dependency injection like the above example even when there aren't multiple implementations since as a project grows, if an auto-wired property fails it can be difficult to track down exactly why. Additionally explicitly showing where dependencies come from can help with organizing your application and visualizing your application hierarchy.
I am pretty new to Spring and I study using "Spring in Action" (fourth edition) by Craig Walls. The interest is not only on how to write code that is working, but also on the correct principles of using Spring.
Regarding the following piece of code from page 142, Listing 5.6:
public class HomeControllerTest {
#Test
public void testHomePage() throws Exception {
HomeController controller = new HomeController();
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
My questions are generated by the following thoughts:
The general understanding is that Spring uses Dependency Injection as a way to reduce the management overhead of object dependencies, increase modularity, simplify testing and code reuse. However, doesn't it imply that beans must be created and managed by the container? Since I started reading on the subject, the first detail that I memorized stated that new should never appear in a well-written piece of code that follows DI.
Could this be a solution in case we want to test a Stateful bean? I mean, if there are multiple independent tests to be run on the same instance, each of them testing the same state of the bean. Even though I found out that there is a suitable annotation for doing this (#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)).
Is there another use case that is difficult or impossible to solve otherwise (except for using new)?
A more 'to the letter' implementation would use #ContextConfiguration to specify the ApplicationContext.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = WebConfig.class)
#WebAppConfiguration
public class HomeControllerTest {
#Autowired
HomeController controller;
#Test
public void testHomePage() throws Exception {
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/")).andExpect(view().name("home"));
}
}
yes, you shouldn't use new to create Spring bean instances (otherwise they're not Spring beans anymore) in production code. But the whole point of DI is to let you create and manually inject your objects with fake dependencies in unit tests. So the test code here is perfectly fine.
Yes, each unit test is free to create its own bean instance and to populate it the way it needs to. Stateful beans are extremely rare, though. They're usually stateless.
Another place where using new to create a Spring bean is precisely in #Bean-annotated methods of configuration classes. The whole point of these methods is precisely to create and initialize the Spring beans that will then be used and injected by Spring. But again, using new in unit tests is absolutely fine, and the right thing to do.
I'm trying to write some unit tests for my controllers in a Spring MVC web app. I have already got a fairly comprehensive set of unit tests for the domain model, but for completeness I want to test the controllers too.
The issue I'm facing is trying to test them without loading a Spring context. I thought I could get around this with mocking, but the method in the controller has a #Transactional annotation which attempts to open a transaction (and fails with a NullPointerException because no Spring context is loaded).
Code example:
public class UsersController {
#Autowired private UserManager userManager;
#Transactional
#RequestMapping(method = RequestMethod.POST)
public ModelAndView create(User user) {
userManager.save(user);
ModalAndView mav = new ModelAndView();
mav.addObject("user", user);
mav.setViewName("users/view");
return mav;
}
}
So essentially I want to test the behaviour without loading a context and actually persisting the user.
Does anyone have any ideas on how I can achieve this?
Cheers,
Caps
I'd say mocking is the way to go here. The #Transactional annotation will have no effect unless there is a Spring context loaded and instructed to configure annotation-based transactions.
Make sure that you aren't instructing JUnit to run your test within a spring context by specifying something like:
#ContextConfiguration(locations = "classpath:spring/ITestAssembly.xml")
#RunWith(SpringJUnit4ClassRunner.class)
To prevent confusion, I keep my unit tests (not running in a spring context) in separate files than my integration tests. Typically all mocking occurs in the unit tests and not in integration tests.
The NullPointerException occurs not because of the Transactional, but because nothing gets injectedas UserManager. You have two options:
run with the spring test runner
mock the userManager and set it.