I have a production class which looks like
#Configurable
public class MyProductionClass {
#Resource private MyResource resource;
private String param;
public MyProductionClass(String param) {
this.param = param
}
public void aMethod() {
resource.doSomething();
//whatever, the previous line throws NullPointerException when testing
}
}
and a test class like
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/test-context.xml"})
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#TestExecutionListeners(listeners = {DependencyInjectionTestExecutionListener.class})
public class MyProductionClassTest {
private MyProductionClass underTest;
#Test
public void aMethodTest() {
underTest = new MyProductionClass("aString");
underTest.aMethod();
}
}
I have some log and I can see Spring context being initialized correctly, with all beans described in test-context.xml happily created, including resource.
But, when the test runs underTest.aMethod() a NullPointerException is thrown saying resource is null.
I use the same initialization (new MyProductionClass("aString")) in production and there everything works flawlessly. Is there something I am missing when using #Configurable classes with jUnit?
Versions are:
Spring 3.2.4
jUnit 4.11
EDIT My understanding of #Configurable: this issue may come from a misunderstanding of this feature. I understood it works so that I am not force to statically declare my #Configurable class in any Spring context (either Java or XML based), but so that I can initialized the class with the new operator and magically dependency are injected in the class. However, I do not know if this can work for tests (and I do not find any reason why it should not, a new is a new, whether it comes from a test class or from a production one)
you must get the bean from spring container
#Autowired
private MyProductionClass underTest;
...
#Test
public void aMethodTest() {
underTest.aMethod();
}
Related
I have a Spring Boot 1.4.2 application. Some code which is used during startup looks like this:
#Component
class SystemTypeDetector{
public enum SystemType{ TYPE_A, TYPE_B, TYPE_C }
public SystemType getSystemType(){ return ... }
}
#Component
public class SomeOtherComponent{
#Autowired
private SystemTypeDetector systemTypeDetector;
#PostConstruct
public void startup(){
switch(systemTypeDetector.getSystemType()){ // <-- NPE here in test
case TYPE_A: ...
case TYPE_B: ...
case TYPE_C: ...
}
}
}
There is a component which determines the system type. This component is used during startup from other components. In production everything works fine.
Now I want to add some integration tests using Spring 1.4's #MockBean.
The test looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyWebApplication.class, webEnvironment = RANDOM_PORT)
public class IntegrationTestNrOne {
#MockBean
private SystemTypeDetector systemTypeDetectorMock;
#Before
public void initMock(){
Mockito.when(systemTypeDetectorMock.getSystemType()).thenReturn(TYPE_C);
}
#Test
public void testNrOne(){
// ...
}
}
Basically the mocking works fine. My systemTypeDetectorMock is used and if I call getSystemType -> TYPE_C is returned.
The problem is that the application doesn't start. Currently springs working order seems to be:
create all Mocks (without configuration all methods return null)
start application
call #Before-methods (where the mocks would be configured)
start test
My problem is that the application starts with an uninitialized mock. So the call to getSystemType() returns null.
My question is: How can I configure the mocks before application startup?
Edit: If somebody has the same problem, one workaround is to use #MockBean(answer = CALLS_REAL_METHODS). This calls the real component and in my case the system starts up. After startup I can change the mock behavior.
In this case you need to configure mocks in a way we used to do it before #MockBean was introduced - by specifying manually a #Primary bean that will replace the original one in the context.
#SpringBootTest
class DemoApplicationTests {
#TestConfiguration
public static class TestConfig {
#Bean
#Primary
public SystemTypeDetector mockSystemTypeDetector() {
SystemTypeDetector std = mock(SystemTypeDetector.class);
when(std.getSystemType()).thenReturn(TYPE_C);
return std;
}
}
#Autowired
private SystemTypeDetector systemTypeDetector;
#Test
void contextLoads() {
assertThat(systemTypeDetector.getSystemType()).isEqualTo(TYPE_C);
}
}
Since #TestConfiguration class is a static inner class it will be picked automatically only by this test. Complete mock behaviour that you would put into #Before has to be moved to method that initialises a bean.
I was able to fix it like this
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyWebApplication.class, webEnvironment = RANDOM_PORT)
public class IntegrationTestNrOne {
// this inner class must be static!
#TestConfiguration
public static class EarlyConfiguration {
#MockBean
private SystemTypeDetector systemTypeDetectorMock;
#PostConstruct
public void initMock(){
Mockito.when(systemTypeDetectorMock.getSystemType()).thenReturn(TYPE_C);
}
}
// here we can inject the bean created by EarlyConfiguration
#Autowired
private SystemTypeDetector systemTypeDetectorMock;
#Autowired
private SomeOtherComponent someOtherComponent;
#Test
public void testNrOne(){
someOtherComponent.doStuff();
}
}
You can use the following trick:
#Configuration
public class Config {
#Bean
public BeanA beanA() {
return new BeanA();
}
#Bean
public BeanB beanB() {
return new BeanB(beanA());
}
}
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {TestConfig.class, Config.class})
public class ConfigTest {
#Configuration
static class TestConfig {
#MockBean
BeanA beanA;
#PostConstruct
void setUp() {
when(beanA.someMethod()).thenReturn(...);
}
}
}
At least it's working for spring-boot-2.1.9.RELEASE
Spring's initialization is triggered before #Before Mockito's annotation so the mock is not initialized at the time the #PostConstruct annotated method is executed.
Try to 'delay' your system detection using #Lazy annotation on the SystemTypeDetector component. Use your SystemTypeDetector where you need it, keep in mind that you cannot trigger this detection in a #PostConstruct or equivalent hook.
I think that it's due to the way you autowire your dependencies. Take a look at this (specially the part about 'Fix #1: Solve your design and make your dependencies visible'). That way you can also avoid using the #PostConstruct and just use the constructor instead.
What U are using, is good for a unit tests:
org.mockito.Mockito#when()
Try to use the following methods for mocking spring beans when the context is spined-up:
org.mockito.BDDMockito#given()
If u are using #SpyBean, then u should use another syntax:
willReturn(Arrays.asList(val1, val2))
.given(service).getEntities(any());
I have this test:
#RunWith(MockitoJUnitRunner.class)
public class myServiceTest {
#InjectMocks
myService subject;
private myService spy;
#Before
public void before() {
spy = spy(subject);
}
#Test
public void testing() {
when(spy.print2()).thenThrow(new RuntimeException()).thenThrow(new RuntimeException()).thenReturn("completed");
spy.print1();
verify(spy, times(3)).print2();
}
and then I have:
#Service("myService")
public class myService extends myAbstractServiceClass {
public String print1() {
String temp = "";
temp = print2();
return temp;
}
#Retryable
public String print2() {
return "completed";
}
}
then I have this interface(which my abstractService implements):
public interface myServiceInterface {
#Retryable(maxAttempts = 3)
String print1() throws RuntimeException;
#Retryable(maxAttempts = 3)
String print2() throws RuntimeException;
}
but, I get a runtimeexception thrown when I run the test, leading me to believe it is not retrying. Am I doing this wrong?
This is because you are not using the SpringJUnitClassRunner.
Mockito and your own classes are not taking the #Retryable annotation in account. So you rely on the implementation of Spring to do so. But your test does not activate Spring.
This is from the SpringJUnit4ClassRunner JavaDoc:
SpringJUnit4ClassRunner is a custom extension of JUnit's BlockJUnit4ClassRunner which provides functionality of the Spring TestContext Framework to standard JUnit tests by means of the TestContextManager and associated support classes and annotations.
To use this class, simply annotate a JUnit 4 based test class with #RunWith(SpringJUnit4ClassRunner.class) or #RunWith(SpringRunner.class).
You should restructure your test class at least to something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=MyConfig.class)
public class MyServiceTest {
#Configuration
#EnableRetry
#Import(myService.class)
public static class MyConfig {}
...
What am I doing there?
activate the Spring JUnit hook
specify the Spring context configuration class
define the spring configuration and import your service as a bean
enable the retryable annotation
Are there some other pitfalls?
Yes, you are using Mockito to simulate an exception. If you want to test this behaviour with Spring like this, you should have a look at Springockito Annotations.
But be aware of that: Springockito you will replace the spring bean completely which forces you to proxy the call of your retryable. You need a structure like: test -> retryableService -> exceptionThrowingBean. Then you can use Springockito or what ever you like e.g. ReflectionTestUtils to configure the exceptionThrowingBean with the behaviour you like.
You should reference the interface type of your service in your test: MyServiceInterface
And last but not least. There is a naming convention nearly all Java developers follow: class names have first letter of each internal word capitalized
Hope that helps.
Another way:
#EnableRetry
#RunWith(SpringRunner.class)
#SpringBootTest(classes={ServiceToTest.class})
public class RetryableTest {
#Autowired
private ServiceToTest serviceToTest;
#MockBean
private ComponentInsideTestClass componentInsideTestClass;
#Test
public void retryableTest(){
serviceToTest.method();
}
}
I think you should let Spring manage the bean, create the appropriate proxy and handle the process.
If you want to mock specific beans, you can create mocks and inject them to the service under test.
1st option could be unwrapping proxied service, creating mocks and manually injecting them:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {RetryConfiguration.class})
#DirtiesContext
public class TheServiceImplTest {
#Autowired
private TheService theService;
#Before
public void setUp(){
TheService serviceWithoutProxy = AopTestUtils.getUltimateTargetObject(theService);
RetryProperties mockRetryProperties = Mockito.mock(RetryProperties.class);
ReflectionTestUtils.setField(serviceWithoutProxy, "retryProperties", mockRetryProperties);
}
#Test
public void shouldFetch() {
Assert.assertNotNull(theService);
}
}
In this example, I mocked one bean, RetryProperties, and injected into the service. Also note that, in this approach you are modifying the test application context which is cached by Spring. This means that if you don't use #DirtiesContext, service will continue its way with mocked bean in other tests. You can read more here
Second option would be creating a test specific #Configuration and mock the depended bean there. Spring will pick up this new mocked bean instead of the original one:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {RetryConfiguration.class, TheServiceImplSecondTest.TestConfiguration.class})
public class TheServiceImplSecondTest {
#Autowired
private TheService theService;
#Test
public void shouldFetch() {
Assert.assertNotNull(theService);
}
#Configuration
static class TestConfiguration {
#Bean
public RetryProperties retryProperties() {
return Mockito.mock(RetryProperties.class);
}
}
}
In this example, we have defined a test specific configuration and added it to the #ContextConfiguration.
#RunWith(SpringJUnit4ClassRunner.class)
public void ITest {
#Autowired
private EntityRepository dao;
#BeforeClass
public static void init() {
dao.save(initialEntity); //not possible as field is not static
}
}
How can I have my service injected already in the static init class?
With Junit 5 you can do this (#BeforeAll instead of #BeforeClass)
public void ITest {
#Autowired
private EntityRepository dao;
#BeforeAll
public static void init(#Autowired EntityRepository dao) {
dao.save(initialEntity); //possible now as autowired function parameter is used
}
}
By leaving the field it means it can be used in other tests
One workaround that I have been using to get this working is to use #Before with a flag to skip it being executed for each testcase
#RunWith(SpringJUnit4ClassRunner.class)
public class BaseTest {
#Autowired
private Service1 service1;
#Autowired
private Service2 service2;
private static boolean dataLoaded = false;
#Before
public void setUp() throws Exception {
if (!dataLoaded) {
service1.something();
service2.somethingElse();
dataLoaded = true;
}
}
}
UPD for Spring 2.x versions.
Spring 2.x supports new feature a SpringExtension for Junit 5 Jupiter, where all you have to do is:
Declare your test class with #ExtendWith(SpringExtension.class)
Inject your #BeforeAll (replacement for #BeforeClass in JUnit 5) with the bean
For example:
#ExtendWith(SpringExtension.class)
...
public void ITest {
#BeforeAll
public static void init(#Autowired EntityRepository dao) {
dao.save(initialEntity);
}
}
Assuming you correctly configured JUnit 5 Jupiter with Spring 2.x
More about it here: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-junit-jupiter-extension
It looks to me that you are trying to populate DB before tests.
I would give a try to two options:
If you can extract initial scripts to sql file (if that is option for you without using repository bean) you can use this approach and annotate your test with #Sql
You can explore DbUnit and here is link to spring dbunit connector which is doing exactly that and helping you populate DB before tests. Here is a github link for integrating between spring test framework and dbunit. After you do that you have #DatabaseSetup and #DatabaseTearDown which will do thing on DB you need
I know that this does not answer how to inject bean in static #BeforeClass but form code it looks it is solving your problem.
Update:
I recently run into same problem in my project and dug out this article which helped me and I think it is elegant way of dealing with this type of problem. You can extend SpringJUnit4ClassRunner with listener which can do instance level setup with all your defined beans.
To answer this question we should recap Spring 2.x versions.
If you want to "autowire" a bean in your #BeforeTest class you can use the ApplicationContext interface. Let's see an example:
#BeforeClass
public static void init() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
EntityRepository dao2 = (EntityRepository) context.getBean("dao");
List<EntityRepository> all = dao2.getAll();
Assert.assertNotNull(all);
}
What's happening: using the ClassPathXmlApplicationContext we are instantiating all beans contained in the application-context.xml file.
With context.getBean() we read the bean specified (it must match the name of the bean!); and then you can use it for your initialization.
You should give to the bean another name (that's the dao2!) otherwise Spring normal "autowired" cannot work on the predefined bean.
As a side note, if your test extends AbstractTransactionalJUnit4SpringContextTests you can do some initialization using executeSqlScript(sqlResourcePath, continueOnError); method, so you don't depend on a class/method that you also have to test separately.
If you just want to use some DB data in your tests, you could also mock the repository and use the #Before workaround Narain Mittal describes:
#RunWith(SpringJUnit4ClassRunner.class)
public void ITest {
#MockBean
private EntityRepository dao;
#Before
public static void init() {
when(dao.save(any())).thenReturn(initialEntity);
}
}
I am still a beginner in Spring . I have one test class as below an run by TestNG ...
#Service("springTest")
public class SpringTest {
private MyService myService;
#Autowired
public void setMyService(MyService myService) {
this.myService = myService;
// below was ok , nothing error
System.out.println(myService.getClass());
}
//org.testng.annotations.Test
#Test
public void doSomethingTest() {
load();
// That may cause NullPointerException
myService.doSomething();
}
private void load(){
// here codes for load spring configuration files
}
}
So , I have no idea why myService.doSomething(); produce NullPointerException ? Can someone give me suggestions what I need to do ? What problem may cause this error ? What am I wrong ? Now I am using as ...
#Service("springTest")
public class SpringTest {
private static MyService myService;
#Autowired
public void setMyService(MyService myService) {
SpringTest.myService = myService;
System.out.println(myService.getClass());
}
//org.testng.annotations.Test
#Test
public void doSomethingTest() {
load();
// fine , without error
myService.doSomething();
}
private void load(){
// here codes for load spring configuration files
}
}
PS: I think I don't need to show my spring configuration file , because it is really simple and I believe that will not be cause any error. So , I left it to describe. But if you want to see , I can show you. And then please assume my spring configurartion file was loaded properly and this was loaded before my Test classes run.
Thanks for reading my question.By the way, I can't also use field
injection and I can only use setter method injection.
Just remove the static initializer. Keep setter injection (I prefer it).
Use your first example, the second code is wrong - don't use it.
Make sure you tests are being run by spring (and therefore the bean is initialized correctly by spring).
The null pointer is caused by running the test method before spring has initialized the bean.
You need somethign like this
#RunWith (SpringJUnit4ClassRunner.class)
#ContextConfiguration (locations = "classpath:/config/applicationContext-test.xml")
public class SpringTest {...}
I have a component setup that is essentially a launcher for an application. It is configured like so:
#Component
public class MyLauncher {
#Autowired
MyService myService;
//other methods
}
MyService is annotated with the #Service Spring annotation and is autowired into my launcher class without any issues.
I would like to write some jUnit test cases for MyLauncher, to do so I started a class like this:
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
#Test
public void someTest() {
}
}
Can I create a Mock object for MyService and inject it into myLauncher in my test class? I currently don't have a getter or setter in myLauncher as Spring is handling the autowiring. If possible, I'd like to not have to add getters and setters. Can I tell the test case to inject a mock object into the autowired variable using an #Before init method?
If I'm going about this completely wrong, feel free to say that. I'm still new to this. My main goal is to just have some Java code or annotation that puts a mock object in that #Autowired variable without me having to write a setter method or having to use an applicationContext-test.xml file. I would much rather maintain everything for the test cases in the .java file instead of having to maintain a separate application content just for my tests.
I am hoping to use Mockito for the mock objects. In the past I have done this by using org.mockito.Mockito and creating my objects with Mockito.mock(MyClass.class).
You can absolutely inject mocks on MyLauncher in your test. I am sure if you show what mocking framework you are using someone would be quick to provide an answer. With mockito I would look into using #RunWith(MockitoJUnitRunner.class) and using annotations for myLauncher. It would look something like what is below.
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher = new MyLauncher();
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
The accepted answer (use MockitoJUnitRunner and #InjectMocks) is great. But if you want something a little more lightweight (no special JUnit runner), and less "magical" (more transparent) especially for occasional use, you could just set the private fields directly using introspection.
If you use Spring, you already have a utility class for this : org.springframework.test.util.ReflectionTestUtils
The use is quite straightforward :
ReflectionTestUtils.setField(myLauncher, "myService", myService);
The first argument is your target bean, the second is the name of the (usually private) field, and the last is the value to inject.
If you don't use Spring, it is quite trivial to implement such a utility method. Here is the code I used before I found this Spring class :
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
Sometimes you can refactor your #Component to use constructor or setter based injection to setup your testcase (you can and still rely on #Autowired). Now, you can create your test entirely without a mocking framework by implementing test stubs instead (e.g. Martin Fowler's MailServiceStub):
#Component
public class MyLauncher {
private MyService myService;
#Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
#Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
#Test
public void someTest() {
}
}
This technique especially useful if the test and the class under test is located in the same package because then you can use the default, package-private access modifier to prevent other classes from accessing it. Note that you can still have your production code in src/main/java but your tests in src/main/test directories.
If you like Mockito then you will appreciate the MockitoJUnitRunner. It allows you to do "magic" things like #Manuel showed you:
#RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
#InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
#Mock
private MyService myService;
#Test
public void someTest() {
}
}
Alternatively, you can use the default JUnit runner and call the MockitoAnnotations.initMocks() in a setUp() method to let Mockito initialize the annotated values. You can find more information in the javadoc of #InjectMocks and in a blog post that I have written.
I believe in order to have auto-wiring work on your MyLauncher class (for myService), you will need to let Spring initialize it instead of calling the constructor, by auto-wiring myLauncher. Once that is being auto-wired (and myService is also getting auto-wired), Spring (1.4.0 and up) provides a #MockBean annotation you can put in your test. This will replace a matching single beans in context with a mock of that type. You can then further define what mocking you want, in a #Before method.
public class MyLauncherTest
#MockBean
private MyService myService;
#Autowired
private MyLauncher myLauncher;
#Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
#Test
public void someTest() {
myLauncher.doSomething();
}
}
Your MyLauncher class can then remain unmodified, and your MyService bean will be a mock whose methods return values as you defined:
#Component
public class MyLauncher {
#Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
A couple advantages of this over other methods mentioned is that:
You don't need to manually inject myService.
You don't need use the Mockito runner or rules.
I'm a new user for Spring. I found a different solution for this. Using reflection and making public necessary fields and assign mock objects.
This is my auth controller and it has some Autowired private properties.
#RestController
public class AuthController {
#Autowired
private UsersDAOInterface usersDao;
#Autowired
private TokensDAOInterface tokensDao;
#RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public #ResponseBody Object getToken(#RequestParam String username,
#RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
And this is my Junit test for AuthController. I'm making public needed private properties and assign mock objects to them and rock :)
public class AuthControllerTest {
#Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
I don't know advantages and disadvantages for this way but this is working. This technic has a little bit more code but these codes can be seperated by different methods etc. There are more good answers for this question but I want to point to different solution. Sorry for my bad english. Have a good java to everybody :)
Look at this link
Then write your test case as
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
#Resource
private MyLauncher myLauncher ;
#Test
public void someTest() {
//test code
}
}