Spring boot #Autowired not working in unit test case - java

As I understand that if we use spring stereotypes then we don't need to use new keyword to create an instance. Spring manages that for us and provide us with the beans at runtime.
And in order for Spring to inject those beans we need to use #Autowired annotation where we want Spring to inject that bean.
Below I have a very simple class where I am using #Component so that spring manages that. This class has one List which I am initializing with my own responsibility and then a small method which does some logic.
#Slf4j
#Data
#NoArgsConstructor
#AllArgsConstructor
#Component
public class Parser {
private List<String> strList = new ArrayList<>();
public void parseStrings(final String[] strs) {
Arrays.stream(strs)
.map(String::toLowerCase)
.filter(str -> str.length() > 8)
.filter(str -> str.endsWith("sam"))
.forEach(sam1 -> { strList.add(sam1); });
}
}
I also wrote one unit test to test that and here is that.
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.*;
#RunWith(MockitoJUnitRunner.class)
class ParserTest {
#Autowired
private Parser parser;
#Test
void parseStrings() {
String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
parser.parseStrings(str);
assertTrue(parser.getStrList().size() == 3);
assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));
}
}
The test fails with
java.lang.NullPointerException when it tries to call parseStrings method which means that its not able to inject a proper initialized bean at run time.
Can some one guide that what I am missing?
Is it necessary to add constructors (which here I am doing using lombok annotations) when using spring stereotypes on a class.

I don't see any mock created so why you are using #RunWith(MockitoJUnitRunner.class)?
I've seen as well answers recommending the use of #SpringBooTest. This annotation loads the whole context of your application basically for integration tests in order to integrate different layers of the application. That also means no mocking is involved. Do you really need that? (I don't think so since you're talking about unit test)
If your parser doesn't reference any other Bean (which need to be mocked), then you are in case of simple unit test.
#RunWith(SpringRunner.class) // you can even removed it
class ParserTest {
private Parser parser;
#Before
public void setUp() {
parser = new Parser();
}
#Test
void parseStrings() {
String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
parser.parseStrings(str);
assertTrue(parser.getStrList().size() == 3);
assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));
}

Spring Autowire if you run the test case with SpringRunner. So modify the test class as follows.
#RunWith(SpringRunner.class)
class ParserTest {
}
To answer your second question,
No, it is not necessary to add no-argument constructor unless you also have a parameterised constructor in the same class. In that case you need to explicitly add a no-arg constructor.

Why so you even need MockitoJUnitRunner here? The Parser has no dependencies. A simple initialization in the test will be enough. Just inialize the Parser instead of using an annotation. #SpringBootTest is meant for integration tests. It brings in the Spring context and makes your unit test slow & bulky.

In my case the class was not declared public

This should work:
#SpringBootTest
#RunWith(SpringRunner.class)
class ParserTest {
#Autowired
private Parser parser;
#Test
void parseStrings() {
String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
parser.parseStrings(str);
assertTrue(parser.getStrList().size() == 3);
assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));
}
}

SpringBoot2,you can use the annotation:#SpringBootTest to do unit test.
just like below case:
#SpringBootTest
class DemoApplicationTests {
#Test
void contextLoads() {
}
}

Related

What exactly is the purpose of SpringExtension?

What exactly SpringExtension do here? Even without that, the test case executes below as expected.
As per the doc doc
SpringExtension integrates the Spring TestContext Framework into JUnit 5's Jupiter programming model.
To use this extension, simply annotate a JUnit Jupiter based test class with #ExtendWith(SpringExtension.class), #SpringJUnitConfig, or #SpringJUnitWebConfig.
Unit Test with SpringExtension
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = SellCarService.class)
class SellCarServiceTest {
#Autowired
private SellCarService carService;
#Test
public void testGetCarId() {
String actual = carService.getCarDetails("1234");
String expected = "PRIUS";
Assertions.assertEquals(expected, actual);
}
}
Simple Unit Test
class SellCarServiceTest {
private final SellCarService carService = new SellCarService();
#Test
public void testGetCarId() {
String actual = carService.getCarDetails("1234");
String expected = "PRIUS";
Assertions.assertEquals(expected, actual);
}
}
If we want to use #Autowired in test classes, then we can go for the first approach or else we can use the simple approach as mentioned later. Is this understanding correct?
Or what is the advantage we get if we use SpringExtension or MockitoExtension?
You get no advantage of using the SpringExtension in this test class as you are not using any Mockito mocks.
You add #ExtendWith(SpringExtension.class) to the test class when you want to use the Mockito annotations such as #Mock, #Mockbean etc.
You are correct regarding #Autowired, there are also other ways to make autowiring work such as using the #SpringBootTest annotation.

How to test a service or do it right mock? Java11, Spock Framework

Colleagues, I welcome you all! Tell me how to decide, or how to act. (Java11, SpringBoot, testing - Spock Framework) I need to write a test that will test a class method, the whole problem is that the method of the class under test calls another service through inheritance, which is not declared in the class under test, but in its abstract ancestor. How to test such a story? If this service were declared in the class under test itself, then everything is clear, I would create a mock in the test and pass it to the constructor, but what if this service is located at the ancestor? I am attaching an example code below.
// The class to be tested
#Service
public class ServiceForTest extends AbstractComponent{
public String methodForTest (String s) {
return someService.generateString(s);
}
}
//An abstract class from which the tested one is inherited and which contains the service
public class AbstractComponent {
#Autowired
protected SomeService someService;
}
public interface SomeService {
String generateString(String s);
}
#Service
public class SomeServiceImpl implements SomeService{
#Override
public String generateString(String s) {
return s;
}
}
And below is an example of what I would do if the service was in the class being tested
//TestClass
#Service
public class ServiceForTest extends AbstractComponent{
final SomeService someService;
public ServiceForTest(SomeService someService) {
this.someService = someService;
}
public String methodForTest (String s) {
return someService.generateString(s);
}
}
class test groovy, Spock Framework
class ServiceForTestTest extends Specification {
ServiceForTest serviceForTest
void setup(){
SomeService someServiceMock = Mock(SomeService)
someServiceMock.generateString("TEST") >> "TEST"
serviceForTest = new ServiceForTest(someServiceMock)
}
def "Test for return current value"(){
when:
def methodForTest = serviceForTest.methodForTest("TEST")
then:
methodForTest == "TEST"
}
}
You use #Autowired, i.e. some kind of dependency injection framework such as Spring or Java EE CDI. Those frameworks have testing support. Specifically for Spring testing, Spock has a Spring module which you can use. I am not a Spring user, so I cannot tell you how to exactly do that, but the documentation is pretty good.
As a general answer, even without any framework support you can test this easily, if you follow the convention to put the test into the same package as the class under test. Because the field you want to inject a mock into is protected, it means for the JVM that all subclasses, but also other classes in the same package have access to it. I.e., you can simply set the value:
serviceForTest = new ServiceForTest()
serviceForTest.someService = someServiceMock
Or, more elegantly using a Groovy-style constructor which implicitly sets field values:
serviceForTest = new ServiceForTest(someService: someServiceMock)
Generally, I recommend constructor or setter injection rather than relying on field injection (especially when fields are private), because then with regard to testability you have a strict dependency to your DI framework and cannot easily write unit tests. So if you can refactor, I recommend you to do it. You just noticed that testing such things can be kind of a headache, unless you have a way out like in this particular case with the protected field. But that is not so super refactoring-friendly.

Overriding #Value in Integration Test

For one of my Spring beans(say Application class), I'm fetching the value of a property(my.property.flag=true/false) from a properties file(prop.properties) using #Value annotation. That works perfectly fine.
I need to write an integration test(say ApplicationIt class) where I need to test with both the values of the property i.e. for both true and false.
In my properties file, the value of the property is set to true. Is it possible to set the value dynamically to false from my Integration test?
For Example,
prop.properties:
my.property.flag=true
Application class file:
#Component
class Application {
//This value is fetched from properties file
//the value is set to true.
#Value(${my.property.flag})
private String isTrue;
......
..........
}
Integration Test:
class ApplicationIT {
//how can I set the value of isTrue here to false?
}
You can specify test properties on the test class as follows:
#RunWith(SpringRunner.class)
#TestPropertySource(properties = {"spring.main.banner-mode=off", "my.property.flag=false"})
public class MyTest {
Since Spring has a whole hierarchy of property overrides, this works pretty well, the downside being you need separate test classes for different values. If you're using Spring Boot, there's another annotation that provides the same functionality but also has more options for configuring your test environment. Example:
#SpringBootTest(properties = {"spring.main.banner-mode=off", "my.property.flag=false"})
Again, you will need separate test classes to handle hard-coded test properties.
I was bugged with this for a while and found this neat way to override the properties. It is quite useful if you need some programmatic initialization of the application context such as registering property sources like in that case but not only. The following approach uses ContextConfiguration's initializers.
example for Spring Boot 1.5.x :
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"management.port=0"})
#ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class)
#DirtiesContext
public abstract class AbstractIntegrationTest {
private static int REDIS_PORT = 6379;
#ClassRule
public static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(REDIS_PORT);
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext ctx) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(ctx,
"spring.redis.host=" + redis.getContainerIpAddress(),
"spring.redis.port=" + redis.getMappedPort(REDIS_PORT));
}
}
}
example for Spring Boot 2.x :
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {"management.port=0"})
#ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class)
#DirtiesContext
public abstract class AbstractIntegrationTest {
private static int REDIS_PORT = 6379;
#ClassRule
public static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(REDIS_PORT);
public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext ctx) {
TestPropertyValues.of(
"spring.redis.host:" + redis.getContainerIpAddress(),
"spring.redis.port:" + redis.getMappedPort(REDIS_PORT))
.applyTo(ctx);
}
}
}
I want to mention good old reflection way. You can use spring provided utility class for it after you wired in your component:
ReflectionTestUtils.setField(component, "isTrue", true)
You can change it to any value you want in consequent tests
Preferably, use constructor injection instead of field injection:
#Component
class Application {
Application(#Value("${my.property.flag}") boolean flag) {
...
}
}
This makes using mocks or test values as simple as passing an argument.

How can I pass an object to my class under test in a UnitTest class?

I want to test a class that has an "ObjectMapper" as an instance variable:
public class EventTransformerImpl implements EventTransformer {
#Inject
private ObjectMapper objectMapper;
private String convertParametersToJson(NotificationConfig config) {
String parametersAsJson = null;
try {
parametersAsJson = objectMapper.writeValueAsString(config.getOrdinaryParameters());
} catch (IOException e) {
logger.info("There was a problem in writing json:" + config.getParameters(), e);
parametersAsJson = null;
}
return parametersAsJson;
}
}
this class does not have any "setter" or "constructor" for initializing "objectMapper" (it is initialized using "spring"):
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd'T'HH:mm:ssZ"/>
</bean>
</property>
</bean>
when I want to test "EventTransformerImpl" class, objectMapper has null value, how can I pass "objectMapper" to "EventTransformerImpl" in my unit test class.
This is a typical example where a mock framework, such as Mockito, can help you:
public class TestClass {
#Mock
private ObjectMapper objectMapper;
#InjectMocks
private EventTransformer eventTransformer;
#BeforeMethod
public void setUp() {
eventTransformer = new EventTransformerImpl();
MockitoAnnotations.initMocks(this);
}
}
Here, #Mock and #InjectMocks are parts of the Mockito framework. The magic happens in MockitoAnnotations.initMocks(this) which will scan this, which in this case is TestClass, for these annotations. Mockito initializes objectMapper as a mock, and injects it into eventTransformer. You can then use Mockito to decide how objectMapper should behave.
You can read more about Mockito here.
Also, #BeforeMethod is a TestNG method, analogue with JUnits #Before.
However, many people prefer constructor injection, as proposed by davidxxx. This will make it clearer which dependencies EventTransformerImpl has, and forces it to be initialized with the correct dependencies. By doing this, there is no need to "magically" (I guess Mockito uses reflection under the hood) inject the dependencies, simply initialize the class to be tested by calling the constructor.
Your class could look something like this (here using Spring's #Autowired annotation):
public class EventTransformerImpl implements EventTransformer {
private ObjectMapper objectMapper;
#Autowired
public EventTransformerImpl(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
private String convertParametersToJson(NotificationConfig config) {
String parametersAsJson = null;
try {
parametersAsJson = objectMapper.writeValueAsString(config.getOrdinaryParameters());
} catch (IOException e) {
logger.info("There was a problem in writing json:" + config.getParameters(), e);
parametersAsJson = null;
}
return parametersAsJson;
}
}
This will be less error prone, because with #InjectMocks, Mockito will silently ignore the injection if it fails, while calling a constructor gives the test class full control of the initialization of the test.
For injecting Spring dependencies in JUnit tests and other objects they depend on I use
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:testBeans.xml" })
public class TestRunner {
#Test
public void test1() {
You can provide in-code bean initialization as well
#RunWith(SpringJUnit4ClassRunner.class)
#Configuration
Read more at https://www.mkyong.com/unittest/junit-spring-integration-example/
All three answers are good; so I don't need to repeat the details of those, but I think it is worth to "align" on the "complete" picture.
As shown Spring has built-in means to do injection for unit tests; and you could just go forward and use that. But of course, that means that you are even more "coupling" your business logic code to the Spring framework.
So, if you ever consider re-using your code in a non-Spring situation, then you will find that not only your production code, but also your unit tests will require major rework in order to be used without Spring.
Thus there is a certain merit in the two other answers that suggest to change your production code to enable you to at least fully test your production code without adding a dependency to Spring here.
1) A class should be testable naturally : why not adding a constructor in EventTransformerImpl to inject the mapper ?
You could do something like that in the spring configuration :
<bean id="EventTransformer" class="EventTransformerImpl">
<constructor-arg ref="objectMapper"/>
</bean>
And in the test you could instantiate EventTransformerImpl with a ObjectMapper mocked dependency in the constructor.
2) The other solution is using reflection to inject the field in the class under test in the test method.
You could use the setField() method ofReflectionTestUtils class of Spring to set the dependency or if you didn't use Spring you could have writen a simple utility method to do the job :
public static final void injectValue(String fieldName, Object fieldValue, Object targetObject) {
try {
Field f = targetObject.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(targetObject, fieldValue);
} catch (Exception e) {
// handle the exception
}
}

Mockito, Testing an object that relies on injected dependencies (Spring)?

I'm new to using Mockito and am trying to understand a way to make a unit test of a class that relies on injected dependencies. What I want to do is to create mock objects of the dependencies and make the class that I am testing use those instead of the regular injected dependencies that would be injected by Spring. I have been reading tutorials but am a bit confused on how to do this.
I have one the class I want to test like this:
package org.rd.server.beans;
import org.springframework.beans.factory.annotation.Autowired;
public class TestBean1 {
#Autowired
private SubBean1 subBean1;
private String helloString;
public String testReturn () {
subBean1.setSomething("its working");
String something = subBean1.getSomething();
helloString = "Hello...... " + something;
return helloString;
}
Then I have the class that I want to use as a mock object (rather than the regular SubBean1 class, like below:
package org.rd.server.beans.mock;
public class SubBean1Mock {
private String something;
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}
I just want to try running a simple test like this:
package test.rd.beans;
import org.rd.server.beans.TestBean1;
import junit.framework.*;
public class TestBean1Test extends TestCase
{
private TestBean1 testBean1;
public TestBean1Test(String name)
{
super(name);
}
public void setUp()
{
testBean1 = new TestBean1();
// Somehow inject the mock dependency SubBean1Mock ???
}
public void test1() {
assertEquals(testBean1.testReturn(),"working");
}
}
I figure there must be some fairly simple way to do this but I can't seem to understand the tutorials as I don't have the context yet to understand everything they are doing / explaining. If anyone could shed some light on this I would appreciate it.
If you're using Mockito you create mocks by calling Mockito's static mock method. You can then just pass in the mock to the class you're trying to test. Your setup method would look something like this:
testBean1 = new TestBean1();
SubBean1 subBeanMock = mock(SubBean1.class);
testBean1.setSubBean(subBeanMock);
You can then add the appropriate behavior to your mock objects for whatever you're trying to test with Mockito's static when method, for example:
when(subBeanMock.getSomething()).thenReturn("its working");
In Mockito you aren't really going to create new "mock" implementations, but rather you are going to mock out the methods on the interface of the injected dependency by telling Mockito what to return when the method is called.
I wrote a test of a Spring MVC Controller using Mockito and treated it just like any other java class. I was able to mock out the various other Spring beans I had and inject those using Spring's ReflectionTestUtils to pass in the Mockito based values. I wrote about it in my blog back in February. It has the full source for the test class and most of the source from the controller, so it's probably too long to put the contents here.
http://digitaljoel.nerd-herders.com/2011/02/05/mock-testing-spring-mvc-controller/
I stumbled on this thread while trying to set up some mocks for a slightly more complicated situation and figured I'd share my results for posterity.
My situation was similar in the fact that I needed to mock dependencies, but I also wanted to mock some of the methods on the class I was testing. This was the solution:
#MockBean
DependentService mockDependentService
ControllerToTest controllerToTest
#BeforeEach
public void setup() {
mockDependentService = mock(DependentService.class);
controllerToTest = mock(ControllerToTest.class);
ReflectionTestUtils.setField(controllerToTest, "dependantService", mockDependentService);
}
#Test
void test() {
//set up test and other mocks
//be sure to implement the below code that will call the real method that you are wanting to test
when(controllerToTest.methodToTest()).thenCallRealMethod();
//assertions
}
Note that "dependantService" needs to match whatever you have named the instance of the service on your controller. If that doesn't match the reflection will not find it and inject the mock for you.
This approach allows all the methods on the controller to be mocked by default, then you can specifically call out which method you want to use the real one. Then use the reflection to set any dependencies needed with the respective mock objects.
Hope this helps someone down the road as it stumped me for a while.

Categories

Resources