I try to use Springs own Dependency Injection in a Junit test case:
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.binarisinformatik.api.AppConfig;
import org.binarisinformatik.satzrechner.SatzRechner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=AppConfig.class)
//#SpringApplicationConfiguration(classes = {AppConfig.class})
public class SatzRechnerTest {
#Autowired
private SatzRechner satzRechner; //SUT
#Before
public void setUp() {
// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SatzRechnerTest.class);
//satzRechner=context.getBean(SatzRechner.class);
}
#Test
public void addiere_satz_4komma6_zu_zahlwert_10() {
assertThat("Addition von \"4,6\" ergibt nicht 10!",
satzRechner.summe("4,6"), is(equalTo(10)));
}
Im testing a class names SatzRechner in which Spring should also autowire some variables. Here is my Class under test:
#Component
public class SatzRechner {
#Autowired //#Inject
private Rechner taschenRechner;
#Autowired
private Zahlenfabrik zahlenfabrik;
public Integer summe(String zeichenSatz) {
return taschenRechner.summe(zahlenfabrik.erzeugeZahlen(zeichenSatz));
}
}
And AppConfig.class which is using as Configurationfile looks like that:
#Configuration
#ComponentScan(value={"org.binarisinformatik"})
public class AppConfig {
}
What is here the problem?
If you want to use a Spring configuration class, this one must have beans definitions. Please find an example below :
Test class:
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.binarisinformatik.api.AppConfig;
import org.binarisinformatik.satzrechner.SatzRechner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=AppConfig.class)
public class SatzRechnerTest {
#Autowired
private SatzRechner satzRechner;
#Test
public void addiere_satz_4komma6_zu_zahlwert_10() {
assertThat("Addition von \"4,6\" ergibt nicht 10!",
satzRechner.summe("4,6"), is(equalTo(10)));
}
}
Configuration class :
You have to declare #Bean annotated methods. These beans are managed by Spring container.
#Configuration
public class AppConfig {
// Beans present here will be injected into the SatzRechnerTest class.
#Bean
public SatzRechner satzRechner() {
return new SatzRechner();
}
#Bean
public Rechner taschenRechner() {
return new TaschenRechner();
}
#Bean
public Zahlenfabrik zahlenfabrik() {
return new Zahlenfabrik();
}
}
Note : I let you properly handle returned types here and beans parameters (if present in your context).
There are two things you have to ensure before you run the test case successfully:
1) Classes SatzRechner, Rechner & Zahlenfabrik should be under "org.binarisinformatik" package
2) Classes Rechner & Zahlenfabrik should also be annotated with #Component as SatzRechner.
Related
I am trying to write test cases for a service I have implemented for a controller in Spring. I have the following Service and Test classes.
StudentCourseRequestService:
import java.util.List;
import org.springframework.stereotype.Service;
import model.CourseRequest;
import repository.ICourseRequestRepository;
import lombok.RequiredArgsConstructor;
#Service
#RequiredArgsConstructor
public class StudentCourseRequestService implements IStudentCourseRequestService {
private final ICourseRequestRepository courseRequestRepository;
#Override
public boolean requestCourse(CourseRequest courseRequest) {
return courseRequestRepository.saveRequest(courseRequest);
}
#Override
public List<CourseRequest> getAllCourseRequests() {
return courseRequestRepository.getAllCourseRequests();
}
#Override
public List<CourseRequest> getAllCourseRequestsOfStudent(Long studentId) {
return courseRequestRepository.getCourseRequestsByStudentId(studentId);
}
}
Test class:
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import model.CourseRequest;
import repository.ICourseRequestRepository;
public class StudentCourseRequestServiceTests {
#MockBean
private ICourseRequestRepository courseRequestRepository;
#InjectMocks
private StudentCourseRequestService studentCourseRequestService;
#Test
public void requestValidCourse() throws Exception {
final CourseRequest courseRequest = new CourseRequest(
"data0",
"data1",
"data2",
"data3",
"data4",
"data5"
);
Mockito.when(courseRequestRepository.saveRequest(courseRequest)).thenReturn(true);
boolean result = studentCourseRequestService.requestCourse(courseRequest);
assertThat(result).isTrue();
}
}
When I run the requestValidCourse() test case, I get the following error:
java.lang.NullPointerException: Cannot invoke "repository.ICourseRequestRepository.saveRequest(model.CourseRequest)" because "this.courseRequestRepository" is null
at service.StudentCourseRequestServiceTests.requestValidCourse(StudentCourseRequestServiceTests.java:29)
at java.base/java.util.ArrayList.forEach(Unknown Source)
at java.base/java.util.ArrayList.forEach(Unknown Source)
How can I resolve this issue?
As #Lesiak recommended, I added the #ExtendWith(MockitoExtension.class) annotation to the top of the class, and changed courseRequestRepository to have the #Mock annotation, instead of the previous #MockBean annotation. The issue got resolved after these changes.
Previously I had the configuration recommended by #Lesiak during my trials to resolve the issue, but I also had the annotation #ExtendWith(SpringExtension.class) at the top of the class. With this configuration, I got the same error. Removing the #ExtendWith(SpringExtension.class) annotation resolved the issue here.
New test class:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
import model.CourseRequest;
import repository.ICourseRequestRepository;
#ExtendWith(MockitoExtension.class)
public class StudentCourseRequestServiceTests {
#Mock
private ICourseRequestRepository courseRequestRepository;
#InjectMocks
private StudentCourseRequestService studentCourseRequestService;
#Test
public void requestValidCourse() throws Exception {
final CourseRequest courseRequest = new CourseRequest(
"data0",
"data1",
"data2",
"data3",
"data4",
"data5"
);
Mockito.when(courseRequestRepository.saveRequest(courseRequest)).thenReturn(true);
boolean result = studentCourseRequestService.requestCourse(courseRequest);
assertThat(result).isTrue();
}
}
I am using spring boot 1.5.x for application. Database is MONGO
Using MongoRepository for crud operations.
Earlier in our project we got dedicated test database instance so we have added mongodb properties of test db in src\test\resources\application.properties.
Now we dont have test db so we want to use embedded/fake db to test our classes Controller/Services/Repository etc..
Options are - embeded mongodb like flapdoodle and fongo
I tried 1 of solution de.flapdoodle.embed.mongo from SO question to use flapdoodle
de.flapdoodle.embed.mongo trying to download zip outside network : unable-to-download-embedded-mongodb-behind-proxy-using-automatic-configuration
I tried fakemongo but its not working for unit and integration testing of controller. Its picking mongo database details of test db using application.properties present in test/resources
pom.xml
<dependency>
<groupId>com.github.fakemongo</groupId>
<artifactId>fongo</artifactId>
<version>${fongo.version}</version>
<!--<scope>test</scope> -->// not working, if removed it says cannot find mongo
</dependency>
<dependency>
<groupId>com.lordofthejars</groupId>
<artifactId>nosqlunit-mongodb</artifactId>
<version>0.7.6</version>
<scope>test</scope>
</dependency>
FakeMongo Configuration
package com.myapp.config;
import com.github.fakemongo.Fongo;
import com.mongodb.MongoClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
#Configuration
#EnableMongoRepositories
public class FakeMongo extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "mockDB";
}
#Bean
public MongoClient mongo() {
Fongo fongo = new Fongo("mockDB");
return fongo.getMongo();
}
}
TestFakeMongo It works. Test executed properly - Reference http://springboot.gluecoders.com/testing-mongodb-springdata.html
package com.myapp.config;
import com.myapp.domain.entitlement.User;
import com.myapp.repository.UserRepository;
import com.lordofthejars.nosqlunit.annotation.UsingDataSet;
import com.lordofthejars.nosqlunit.core.LoadStrategyEnum;
import com.lordofthejars.nosqlunit.mongodb.MongoDbRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static com.lordofthejars.nosqlunit.mongodb.MongoDbRule.MongoDbRuleBuilder.newMongoDbRule;
import static org.junit.Assert.assertTrue;
#RunWith(SpringRunner.class)
#Import(value = {FakeMongo.class})
public class TestFakeMongo {
#Autowired
private ApplicationContext applicationContext;
#Rule
public MongoDbRule embeddedMongoDbRule = newMongoDbRule().defaultSpringMongoDb("mockDB");
#MockBean
private UserRepository userRepository;
#Test
#UsingDataSet(loadStrategy = LoadStrategyEnum.DELETE_ALL)
public void getAllUsers_NoUsers() {
List<User> users = userRepository.findAll();
assertTrue("users list should be empty", users.isEmpty());
}
}
UserRepositoryTest - Unit testing for Repository also working using fongo Reference - http://dontpanic.42.nl/2015/02/in-memory-mongodb-for-unit-and.html
package com.myapp.repository;
import com.myapp.config.SpringUnitTest;
import com.myapp.domain.User;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
public class UserRepositoryTest extends SpringUnitTest {
#Autowired
private UserRepository userRepository;
#Before
public void setup() {
importJSON("user", "user/user.json");
}
#Test
//#UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT, locations = "/json-data/user/user.json") // test/resources/..
public void findUser_should_return_user() {
User user = userRepository.findByXYZId("XX12345");
assertNotNull(user);
}
#Test
public void findUser_should_return_null() {
User user = userRepository.findByXYZId("XX99999");
assertNull(user);
}
#Test
public void deleteUser_should_return_null() {
userRepository.delete("XX12345");
User user = userRepository.findByXYZId("XX12345");
assertNull(user);
}
#Test
public void saveUser_should_return_user() {
User user = new User();
user.setXYZId("XX12345");
user.setAck(true);
List<DateTime> dateTimeList = new ArrayList<>();
dateTimeList.add(DateTime.now(DateTimeZone.UTC));
user.setAckOn(dateTimeList);
User dbUser = userRepository.save(user);
assertEquals(user, dbUser);
}
#Test
public void findAllUser_should_return_users() {
List<User> userList = userRepository.findAll();
assertEquals(1, userList.size());
}
}
Controller level testing not working.. Reference- https://www.paradigmadigital.com/dev/tests-integrados-spring-boot-fongo/
UserControllerTest failed java.lang.IllegalStateException: Failed to load ApplicationContext - At this point test mongo db details fetech instead of fongo test\resources\application.properties test server db details are loading
package com.myapp.config;
package com.myapp.controllers;
import com.myapp.config.FakeMongo;
import com.myapp.services.persistance.UserService;
import com.lordofthejars.nosqlunit.mongodb.MongoDbRule;
import org.junit.Before;
import org.junit.Rule;
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.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import static com.lordofthejars.nosqlunit.mongodb.MongoDbRule.MongoDbRuleBuilder.newMongoDbRule;
import static org.hamcrest.CoreMatchers.containsString;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#ActiveProfiles("it")
#RunWith(SpringRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = Application.class
)
#Import(value = {FakeMongo.class})
#AutoConfigureMockMvc
#TestPropertySource(locations = "classpath:application-it.properties")
public class UserControllerTest {
#Rule
public MongoDbRule embeddedMongoDbRule = newMongoDbRule().defaultSpringMongoDb("mockDB");
#Autowired
private MockMvc mockMvc;
#MockBean
private UserService service;
#Before
public void setUp() throws Exception {
}
#Test
public void deleteUser() throws Exception {
Mockito.when(this.service.deleteUser(Mockito.anyString())).thenReturn(true);
this.mockMvc.perform(delete("/User")).andDo(print()).andExpect(status().isOk())
.andExpect((ResultMatcher) content().string(containsString("true")));
}
}
UserControllerTest Not working, failing with error java.lang.IllegalStateException: Failed to load ApplicationContext as its tries to connect to test instance of mongo database using application.properties present in test/resources
Appreciate working example to use fakemongo while running Integration test for Controller level
What changes, I need to do in code level(Controller class or any other class) or application.properties of test\resources ?
Can you please provide me with an example of primitive type dependency injection in spring boot. I have tried once but TestConfiguration which is my custom bean definition class does not detect or does not recognize by spring boot application.
here is my code,
//Engine class
package com.test2.projectTest;
import org.springframework.stereotype.Component;
#Component
public class Engine {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
//Test Configuration
package com.test2.projectTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class TestConfiguration {
#Bean("engine")
public Engine engine(){
Engine engine = new Engine();
engine.setMsg("Message is injected");
return engine;
}
}
//spring main application
package com.test2.projectTest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
public class ProjectTestApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectTestApplication.class, args);
}
}
//JUnit Test
package com.test2.projectTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class ProjectTestApplicationTests {
#Test
public void contextLoads() {
ApplicationContext apc = new
AnnotationConfigApplicationContext(TestConfiguration.class);
Engine e = (Engine) apc.getBean("engine");
e.getMsg();
}
}
// Output - org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'engine' available
Please suggest any solution to above issue
Add #componentscan annotation in main class and provide engine class package and it should work
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
#ActiveProfiles(profiles = "test")
#RunWith(MockitoJUnitRunner.class)
public class ConfigurationTest {
#Autowired
private Environment environment;
#Test
public void test1() throws Exception {
environment.getProperty("bhavya",Boolean.class);
}
#Configuration
#Profile("test")
#ComponentScan(basePackages={"com.bhavya.test"})
public static class EnvironmentServiceTestConfiguration{
#Bean
#Primary
public Environment environment(){
return Mockito.mock(Environment.class);
}
}
}
I also tried putting EnvironmentServiceTestConfiguration as a non-inner non-static class, but didn't help.
Here is what I tried in a separate class:
#RunWith(MockitoJUnitRunner.class)
#Profile("test")
#Configuration
class EnvironmentServiceTestConfiguration{
#Bean
#Primary
public Environment environment(){
return Mockito.mock(Environment.class)
}
}
didn't work either
The test class is located in test/java/com.bhavya.test package.
I am trying to run this particular test test1
This is my first test of such kind. I have never before used AnnotationConfigContextLoader.class, enlighten me.
Stacktrace :
java.lang.NullPointerException
at Line number where I have statement :
environment.getProperty("bhavya",Boolean.class);
Try using #RunWith(SpringJUnit4ClassRunner.class)
Here is an example explaining how can be done
https://www.mkyong.com/unittest/junit-spring-integration-example/
Here is a code sample that works for the sample in the question:
package com.example.demo.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
#RunWith(SpringJUnit4ClassRunner.class)
public class ConfigurationTest {
#Autowired
private Environment environment;
#Test
public void test1() throws Exception {
System.out.println(environment.getProperty("bhavya",Boolean.class));
}
#Configuration
#ComponentScan(basePackages={"com.example.demo.test"})
public static class EnvironmentServiceTestConfiguration{
#Bean
#Primary
public Environment environment(){
Environment env = Mockito.mock(Environment.class);
when(env.getProperty(eq("bhavya"), eq(Boolean.class))).thenReturn(true);
return env;
}
}
}
Try below annotations for your test class
#ContextConfiguration(classes = Config.class)
#SpringBootTest
#ExtendWith(SpringExtension.class)
#RunWith(SpringJUnit4ClassRunner.class)
This test is failing but I don't know why or how to fix it. If I hit a break point and call mockingContext.getBean(Repository.class), it does return my mock object, but for some reason the ProductionCode returned in createProductionCodeWithMock still has the real Repository autowired into it. I can only suspect that I need to do something special/extra to have an effect on autowired beans, but I don't know what it is. Why isn't this test passing, and how can I make it pass?
For what it's worth, I'm well aware of all the answers to this question. I still want to know why this test isn't working. I am using Spring 3.x.
Here's the test:
package mavensnapshot;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class ProductionCodeTest {
#Test
public void testReplaceRepositoryWithMock() {
Repository mockRepository = mock(Repository.class);
ProductionCode productionCode = createProductionCodeWithMock(mockRepository);
productionCode.doSomething();
verify(mockRepository).save();
}
private ProductionCode createProductionCodeWithMock(Repository mockRepository) {
GenericApplicationContext mockingContext = new GenericApplicationContext();
mockingContext.getBeanFactory().registerSingleton(Repository.class.getName(), mockRepository);
mockingContext.setParent(new ClassPathXmlApplicationContext("/beans.xml"));
return mockingContext.getBean(ProductionCode.class);
}
}
Here's my production code:
package mavensnapshot;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("/beans.xml");
ProductionCode productionCode = context.getBean(ProductionCode.class);
productionCode.doSomething();
}
}
package mavensnapshot;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class ProductionCode {
#Autowired
private Repository repository;
public void doSomething() {
repository.save();
}
}
package mavensnapshot;
import org.springframework.stereotype.Service;
#Service
public class Repository {
public void save() {
System.out.println("production code");
}
}