I'm writing service tests for my Spring Boot Web application that acts as an interface to MongoDB. Ideally, my service test will test each component of my Spring application before finally hitting a Mocked MongoTemplate. The following code uses a MockMvc to hit my web endpoints.
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
#AutoConfigureDataMongo
public class MyControllerServiceTest {
#Autowired
private MockMvc mvc;
#Autowired
private MongoTemplate mongoTemplate
#SpyBean
private MyMongoRepository myMongoRepository;
#Test
public void createTest() {
MyObject create = new MyObject()
given(this.myMongoRepository.insert(create));
this.mvc.perform(post("localhost:8080/myService")...)...;
}
}
MyController contains an #Autowired MyMongoRepository, which in turn implements MongoRepository and necessitates a mongoTemplate bean. This code executes properly only if a running MongoDB instance can be found (this example is more of an integration test between my service and MongoDB).
How can I mock out MongoTemplate while still using my MockMvc?
You need to add the following line to your test unit:
#MockBean
private MongoTemplate mongoTemplate;
For example, your class should look like this:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class, excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
public class MyMvcTests {
#Autowired
private MockMvc mvc;
#MockBean
private MyRepository repository;
#MockBean
private MongoTemplate mongoTemplate;
#Test
public void someTest() {}
}
You can find a complete Spring Boot application that include Integration and Unit tests here.
I think a better approach to test would be to test your web layer(controller) and your service layer separately.
For testing your web layer you can use MockMvc and you can mock your service layer.
For testing your service layer which in turn talks to mongo, you can use a Fongo and nosqlunit.
Some examples here
https://arthurportas.wordpress.com/2017/01/21/sample-project-using-spring-boot-and-mongodbfongo-and-test-repository-with-nosqlunit/
https://github.com/JohnathanMarkSmith/spring-fongo-demo
Related
I am trying to test my controller method which is calling method of a dao class , now dao class has #Autowired repositry and method of dao class is also calling the repositry method.
I am ended up #mock repository in my test class too for mocking its behaviour.
test class:
Mock
UserRepository userRepository;
Mockito.when(userRepository.findByEmailId("abc#gmail.com")).thenReturn(obj);
dao class:
#Autowired
UserRepository userRepository;
obj=userRepository.findByEmailId(email);
now, my test case is not running. i am trying to create a base class for all the #Autowiring.
What should i do?
You don't need to mock UserRepository, but the DAO instead which is the direct dependency of your Controller. Something like the following:
#ExtendWith(SpringExtension.class)
#WebMvcTest(ControllerClassName.class)
public class ControllerClassNameTest {
#Autowired
protected MockMvc mvc;
#MockBean
private DaoClassName dao;
#BeforeEach
public void setUp() throws Exception {
Mockito.when(dao.whateverMethodYouWantToMock()).thenReturn(xyz);
}
// Your tests here
}
Keep in mind that you should test your Controllers with MockMvc to emulate a real HTTP call and not a direct method call. More information at https://www.baeldung.com/spring-boot-testing#unit-testing-with-webmvctest.
Am developing MicroServices in springBoot. Am writing unit test for Service and DAO layer. When I use #SpringBootTest it starting application on build. But It should not start application
when I run unit test. I used #RunWith(SpringRunner.class), But am unable to #Autowired class instance in junit class. How can I configure junit test class that should not start application and how to #Autowired class instance in junit class.
Use MockitoJUnitRunner for JUnit5 testing if you don't want to start complete application.
Any Service, Repository and Interface can be mocked by #Mock annotation.
#InjectMocks is used over the object of Class that needs to be tested.
Here's an example to this.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public class AServiceTest {
#InjectMocks
AService aService;
#Mock
ARepository aRepository;
#Mock
UserService userService;
#Before
public void setUp() {
// MockitoAnnotations.initMocks(this);
// anything needs to be done before each test.
}
#Test
public void loginTest() {
Mockito.when(aRepository.findByUsername(ArgumentMatchers.anyString())).thenReturn(Optional.empty());
String result = aService.login("test");
assertEquals("false", result);
}
With Spring Boot you can start a sliced version of your application for your tests. This will create a Spring Context that only contains a subset of your beans that are relevant e.g. only for your web layer (controllers, filters, converters, etc.): #WebMvcTest.
There is a similar annotation that can help you test your DAOs as it only populates JPA and database relevant beans (e.g. EntitiyManager, Datasource, etc.): #DataJpaTest.
If you want to autowire a bean that is not part of the Spring Test Context that gets created by the annotatiosn above, you can use a #TestConfiguration to manually add any beans you like to the test context
#WebMvcTest(PublicController.class)
class PublicControllerTest {
#Autowired
private MockMvc mockMvc;
#TestConfiguration
static class TestConfig {
#Bean
public EntityManager entityManager() {
return mock(EntityManager.class);
}
#Bean
public MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
}
}
Depending your test setup, if you don't want to autowire a mock but the "real thing", You could simply annotate your test class to include exactly the classes you need (plus their transitive dependencies if necessary)
For example :
#SpringJUnitConfig({ SimpleMeterRegistry.class })
or
#SpringJUnitConfig
#Import({ SimpleMeterRegistry.class })
or
#SpringJUnitConfig
#ContextConfiguration(classes = { SimpleMeterRegistry.class })
See working JUnit5 based samples in here Spring Boot Web Data JDBC allin .
I have following service class, that I want to test
#Service
public class MasterService {
#Inject
private ServiceOne serviceOne;
#Autowired
private ServiceTwo serviceTwo;
#Inject
private ServiceThree serviceThree;
#VisibleForTesting
void execute() {
if (serviceThree.isFlag()) {
....
}
}
I am testing execute() method. I want to mock serviceThree.isFlag() to return true. Following is my test.
public class MasterServiceIT{
#Inject
private MasterService masterService;
#Inject
private ServiceThree serviceThree;
#Test
public void testMasterService() {
when(serviceThree.isFlag()).thenReturn(true); <---- this never works
masterService.execute();
}
}
However, it never retrieves true. Any remarks? I wanted to use #InjectMocks Then can I inject only this service which I mocked? or I need to mock each service if I am using #InjectMocks
Are you sure that you need to mock for an integration test? Well, sometimes there are cases when we need to mock some service which refers to some external services like SharePoint, etc. Well if you need to mock so in this case you need to mock spring service bean in spring context. You can do it via #MockBean
i have a #Service that I am trying to mock in an Unit Test but i get a null value so far. In the application class I specify what are the scanBasePackages. Do I have to do this in a different way? Thanks.
This is my service class that implements an interface:
#Service
public class DeviceService implements DeviceServiceDao {
private List<Device> devices;
#Override
public List<Device> getDevices(long homeId) {
return devices;
}
}
This is my unit test.
public class SmartHomeControllerTest {
private RestTemplate restTemplate = new RestTemplate();
private static final String BASE_URL = “..”;
#Mock
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
Device activeDevice = new DeviceBuilder()
.getActiveDevice(true)
.getName("Alexa")
.getDeviceId(1)
.getHomeId(1)
.build();
Device inativeDevice = new DeviceBuilder()
.getInactiveDevice(false)
.getName("Heater")
.getDeviceId(2)
.getHomeId(1)
.build();
UriComponentsBuilder builder = UriComponentsBuilder
.fromUriString(BASE_URL + "/1/devices");
List response = restTemplate.getForObject(builder.toUriString(), List.class);
verify(deviceService, times(1)).getDevices(1);
verifyNoMoreInteractions(deviceService);
}
You have to use a Spring test runner if you want to load and use a Spring context during tests execution.
You don't specify any runner, so it uses by default the runner of your test API. Here is probably JUnit or TestNG (the runner using depends on the #Test annotation specified).
Besides, according to the logic of your test, you want to invoke the "real"
REST service :
List response = restTemplate.getForObject(builder.toUriString(),
List.class);
To achieve it, you should load the Spring context and load the Spring Boot container by annotating the test with #SpringBootTest.
If you use a Spring Boot context, to mock the dependency in the Spring context, you must not use #Mock from Mockito but #MockBean from Spring Boot.
To understand the difference between the two, you may refer to this question.
Note that if you are using the #SpringBootTest annotation, a TestRestTemplate is automatically available and can be autowired into your test.
But beware, this is fault tolerant. It may be suitable or not according to your tests.
So your code could look like :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class SmartHomeControllerTest {
private static final String BASE_URL = “..”;
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private DeviceService deviceService;
#Test
public void getHomeRegisteredDevices() throws Exception {
...
}
As a side note, avoid using raw type as List but favor generic type.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = NotificationApplication.class)
public class EmailClientImplTest {
...
}
And also add the needed properties/configs in
/src/test/resources/application.yml
Good luck!
I figured it out, I am using Mockito and used that to annotate my test class. This allowed me to get a mock of the service class that i am trying to use.
#RunWith(MockitoJUnitRunner.class)
public class SmartHomeControllerTest {..
#Mock
private DeviceService deviceService;
}
Try with #InjectMock instead of #Mock
You should run your test with spring boot runner
I am developing a JUnit test into a Spring Controller, the Controller calls a bunch of Services. My JUnit test code is above:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebApplicationConfiguration.class, DatabaseConfiguration.class, ServiceConfiguration.class})
#WebAppConfiguration
public class ProcessFileControllerTest {
private MockMvc mockMvc;
#Test
public void getPageTest() throws Exception{
final ProcessFileController controller = new ProcessFileController();
SecurityHelperUtility.setupSecurityContext("user", "pass", "ROLE_ADMIN");
mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get(URI.create("/processFile.html")).sessionAttr("freeTrialEmailAddress", "")).andExpect(view().name("processFile"));
}
When I run the JUnit test, I get a NullPointerExcelption when I call this particular line of code of my ProcessFileController.java
final Company company = companyService.getCompanyByUser(userName);
I can see that the Serviceis Null.
In my ProcessFileController.java the Service is being declared as:
#Autowired
private CompanyService companyService;
Any clue? Thanks in advance.
You are creating your controller using the new keyword. It should be a spring managed bean for spring to inject its dependencies. Use #Autowired
#Autowired
ProcessFileController controller;