NullPointerException when trying to autowire configuration properties running with MockitoJUnitRunner - java

I have been trying to use configuration properties in my test classes but couldn't find the way to do so as I always get NullPointerException.
application.yaml
affix:
lover: 'interests'
social: 'social_media'
YamlConfig.java
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties
#EnableAutoConfiguration
#Data
public class YamlConfig {
private HashMap<String, String> affix;
}
Service.java
#Autowired
private YamlConfig config;
...
setFeatureName(config.getAffix().get("social"));
// supposed to return social_media
The code above is working fine in my service but when I tried to use configuration properties in my test classes, it didn't work.
ServiceTest.java
#RunWith(MockitoJUnitRunner.class)
public class MetadataServiceTest {
#Autowired
private YamlConfig config;
#Test
public void testPropertiesNotNull() {
assertNotNull(config.getAffix().get("social"));
}
I've also tried other annotations as well, but none of them seemed to work. Most of the example are running test using JUnitRunner, and I'm not sure if that's the reason why they didn't work on my test classes.
Is there anyway to get configuration properties to use in test class using MockitoJUnitRunner without mocking the whole thing (the actual config is very large and would be hard to mock result for each one)?

Your #Autowired in test is ignored, as you don't have any Spring context selected. Make it an integration Spring test with annotation.

Since you're using #Autowired annotation, you should use for ex: #RunWith(SpringJUnit4ClassRunner.class)
In that way you will start your tests in spring context.
But if you still want to use MockitoJUnitRunner, instead of using #Autowired you can use:
#InjectMocks
private YamlConfig config;

Related

Spring #WebFluxTest is loading #Component s

I am trying to create a #WebFluxTest in spring to test my controllers.
#WebFluxTest(value = {MyController.class})
public class MyControllerTest {
#MockBean
private MyService service;
#Autowired
private WebTestClient webTestClient;
#Test
void testSomething() {
...
}
However, when I execute the test, I get a lot of: org.springframework.beans.factory.NoSuchBeanDefinitionException for dependencies of #Component s. Meaning that, Spring is trying to find dependencies of #Component when it should ignore those.
I understand that if I use #WebFluxTest, spring should not scan the classpath for any #Component.
My Application class is only annotated with #SpringBootApplication.
What could I be missing here?
SOLUTION UPDATE:
So, I know what was happening. Actually, the class that I had annotated with #Component was an implementation of a WebFilter, and if I check the filter configured for a WebFluxTest (WebFluxTypeExcludeFilter) it adds WebFilter to the include part.
That is why Spring was picking it up.
The error that you're getting could be exactly due to the annotation #WebFluxTest not loading your #Component classes. Could it be that your MyService is instantiating any object that has #Component? Maybe your MyController
Exemplifying, supposing your MyService is like that:
#Service
#AllArgsConstructor
public class MyService {
private final MyRepository repository;
private final Env env;
public void insert(...) {
System.out.println(env.getApplicationName() + " random stuff");
...
}
}
And your Env is annotated with #Component, you will get the same error (NoSuchBeanDefinitionException) if you try to use any method from the Env class since it's null due to #MockBean in MyControllerTest.
The same goes for MyController if it's instantiating any #Component object
If that is the case, then in your MyControllerTest you could try adding #Import(Env.class) or even trying to use when() from mockito with a .thenReturn()
If all that doesn't work, could you please provide more info about your error log and service/controller classes?
So, I know what was happening. Actually, the class that I had annotated with #Component was an implementation of a WebFilter, and if I check the filter configured for a WebFluxTest (WebFluxTypeExcludeFilter) it adds WebFilter to the include part. That is why Spring was picking it up.

passing properties field using #value annotation to mocked method is null

I am unable to pass the field by reading from application-test.properties file from test to the mocked method.
#RunWith(SpringRunner.class)
#TestPropertySource("classpath:application-test.properties")
public class ReportImplTest {
#Mock
private Dependencies dependencies;
#InjectMocks
private ReportImplTest underTest;
#Test
public void testgetReports() {
List<String> reps= underTest.getReports(anyString());
}
}
Here is the actual class of the mocked method
#Component
public class ReportImpl {
#Value("${REP_PROPNAME}")
String reppropname;
public List<String> getReports(String rep){
return staticUtilityclass.process(staticUtilityclass.transform(reppropname,"Reports"));
}
}
reppropname is coming as null in the getReports method. Test is executing in test context wheres the ReportImpl class will be in application context. Is there a way to get the value of the reppropname.
I tried used #ContextConfiguration (#ContextConfiguration(classes={ApplicaitonBootStarter.class)}
it is working , but it loads all the beans and dependencies.
Any other way to get the reppropname?
The reason why the value is not injected here is that you don't provide the configuration to your test class. Spring just doesn't know how to build your bean.
So, as you mentioned you have to annotate the test class with #ContextConfiguration. If you don't want to build the entire context with all the beans, you can provide create a test configuration and provide there only the needed beans.
#Configuration //can be as well annotated with #TestConfiguration
#ComponentScan("package.to.scan")
public class TestConfiguration {
}
And now provide this class to your test
#RunWith(SpringRunner.class)
#TestPropertySource("classpath:application-test.properties")
#ContextConfiguration(classes = TestConfiguration.class)
public class ReportImplTest {
........
}
But there is one more thing. Assuming that you have a #Before method that performs MockitAnnotations.initMocks(this);, you still have your object-under-test declared only with #InjectMocks. What does it mean? It means that if you don't initialize this object by yourself, mockito will take care of it and will initialize with using the available constructor, and in this case, spring won't inject the #Value annotated field. What you need to do, is to annotate you object-under-test with #Autowired so spring will initialize it before mockito will try to take care of it:
#InjectMocks
#Autowired
private ReportImplTest underTest;

ServletContext cannot open properties file when executing JUnit test

I'm testing a REST controller using JUnit 4 and MockMvc. When I've written the test a few weeks ago, everything worked as expected. I've done some modifications in my code but I didn't change the JUnit test. Now, when I'm trying to run my tests, I have the error:
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/application.properties]
Here is my code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyServerApplication.class)
#SpringBootTest
#Transactional
public class MovieControllerTest {
private MockMvc mockMvc;
#Autowired
private MovieRepository movieRepository;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
// Some tests
}
And my main class:
#SpringBootApplication
public class MyServerApplication{
public static void main(String[] args) {
SpringApplication.run(MyServerApplication.class, args);
}
}
My application.properties file is located in src/main/resources. I didn't move this file, I didn't do anything but add some code in my services and add some properties in my file.
I read SO questions & doc, and tried these solutions:
Check that src/main/resources is still in my test classpath
Add #PropertySource("classpath:application.properties") under the annotations in my test ; it didn't work so I tried to create a src/test/resources with a copy of application.properties inside, as suggested in one post
Add #PropertySource("classpath:application.properties") in the main class instead of the test class
Add #WebAppConfiguration annotation
Add #WebMvcTest annotation
I didn't try all of these solutions at the same time of course, I removed the added code after each failure.
I can still run my code without any issue though, only the test class results in FileNotFoundException.
How to solve this? And why do I have an issue with the test class but everything working fine when I run my server?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyServerApplication.class)
#SpringBootTest
#Transactional
public class MovieControllerTest { ... }
This is what you have on your test class. When using #SpringBootTest you shouldn't be using #ContextConfiguration (see testing chapter of the Spring Boot Reference Guide).
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#Transactional
public class MovieControllerTest { ... }
I would also suggest you use Spring Boot for testing instead of trying to do things manually. For mock mvc testing Spring Boot applications there are special slices and setup already done for you.
To enable this add #AutoConfigureMockMvc to your test and put #Autowired on the MockMvc field (and remove the setup in your #Before method).

When running test class, properties can't be read properly from .properties file by using #Value annotation, in Spring Maven project

//Update
After viewing helpful comments, I realize the problem should then be, how to unit test method using values read from properties by #Value .
//
I am working on this issue for days, I am writing unit test for a serviceClass.The serviceClass is like below :
import ...
#Component
public class ServiceClass implements ServiceInterface {
#Value("${data.layer.url}")
private String dataLayerUrl;
#Autowired
private RestTemplate restTemplate
public void dummy(){
restTemplate.postForObject(dataLayerUrl + "/" + ... , ...);
}
}
And CONFIG_DIR is already defined in application configuration file.
I have a SomeConfig class defining beans as below. (...src/main/java/com.app/configuration/SomeConfig)
#Configuration
#ComponentScan(basePackages = {"..."})
#PropertySource(value = "file:${CONFIG_DIR}/app.properties")
public class SomeConfig{
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
...
return restTemplate;
}
}
My test class is as below:
Import ...
#Profile("test")
public class ServiceClassTest extends AbstractTest {
#Value("${data.layer.url}")
private String dataLayerUrl;
#InjectMocks
private ServiceClass ServiceClass;
#Mock
RestTemplate restTemplate;
#Before
public void initializeMockito() {
MockitoAnnotations.initMocks(this);
}
#Test
public void dummyTest(){
when(restTemplate.postForObject(dataLayerUrl + "/" + ..., ...)).thenReturn(...);
serviceClass.dummy();
assertEquals(...);
verify(restTemplate).postForObject(...);
}
}
And then my AbstractTest as below :
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("test")
#SpringApplicationConfiguration(classes = Application.class)
#ContextConfiguration(classes = {TestConfiguration.class})
#ComponentScan(basePackages = ...)
public abstract class AbstractTest {
protected Logger logger = LoggerFactory.getLogger(this.getClass());
}
And I also have .../src/test/resources/application-test.properties defined as below
#Datalayer properties
data.layer.url=http://camel-dev-01.xxx.com:5001
This is the same as defined in application.properties(which locates outside of project in CONFIG_DIR.
The logic of testing is just to make sure when you call dummy method of serviceClass, the postForObject method of restTemplate is called exactly once.
But when doing it this way, I am facing with 2 problems.
when I run test class in debug mode, I found
in ServiceClassTest. dataLayerUrl = "$data.layer.url"
in ServiceClass. dataLayerUrl = null
I researched around and be able to solve problem one by following this link
https://gist.github.com/danlangford/3418696
But this is not an ideal way to do this, since by default spring should be able to read properties from application-test.properties.
And I never figured out what caused the second issue and how to solve it.
I think this would be a common issue when writing unit test on class which read properties from .properties file using $Value annotation. Any comments or suggestions would be very much appreciated.
The key point hear as said M. Deinum is that you use a mix of Spring bean and Mock Object that in this case aren't Spring bean and for this reason can't benefit of the feature of Spring Container such as the injection of the properties.
In particular you should use the spring test abstraction as a "integration test" istruments. With this words I intended that you should use this abstraction, for test the correct configuration, behavior and so on fo your bean in the spring contex. However if you use Stub or mock object you actually exit, of a smal part probably, by the management of spring and the your test don't make sense. Using stub or mock the your test become a Unit test in sense that it will be a test the your bean and functionality in isolation infact you have mock or stub the dependency of your object.
I hope that this reflection could be help you
I am glad to know there is no way to read values from properties by #Value inside a mock obj.
But still my problem is that I want to unit test my dummy method in ServiceClass. Put it another way, as long as I could unit test this method, I don't care whether #Value works or not.
Here is my solution of test method
#Profile("test")
public class ServiceClassTest extends AbstractTest {
#Value("${data.layer.url}")
private String dataLayerUrl;
#InjectMocks
private ServiceClass ServiceClass;
#Mock
RestTemplate restTemplate;
#Before
public void initializeMockito() {
MockitoAnnotations.initMocks(this);
}
#Test
public void dummyTest(){
when(restTemplate.postForObject(anyString() , eq(), eq() )).thenReturn(...);
serviceClass.dummy();
assertEquals(...);
verify(restTemplate).postForObject(anyString(), eq(), eq());
}
By using anyString, I don't rely on what value is read from properties, since I only want to test whether dummy method call restTemplate's postForObject method properly.
You need to add PropertySourcesPlaceholderConfigurer to your test configuration in order to populate properties annotated with #Value annotation. Spring Boot adds it to configuration, but since your test is running without Spring Boot you have to declare it. For more details seehere .
in place of #InjectMocks you can write #Autowired and I thing you can use both annotation like that
Case 1
#InjectMocks
private ServiceClass ServiceClass;
case 2
#Autowired
#InjectMocks
private ServiceClass ServiceClass;
I have same issue but after discussion my senior I have find above like solutions

Injecting Mockito Mock objects using Spring JavaConfig and #Autowired

I'm trying to replace an #Autowired object with a Mockito mock object. The usual way of doing this was with xml using Springockito:
<mockito:mock id="SomeMock" class="com.package.MockInterface" />
Currently I'm trying to move over to using Spring's JavaConfig to do the job. All of a sudden the Java expressions are a whole lot more verbose than xml:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class MyTestClass {
#Configuration
static class Config {
#Bean
public MockInterface somethingSpecial() {
return Mockito.mock(MockInterface.class);
}
}
#Autowired MockInterface mockObj;
// test code
}
I discovered a library called Springockito-annotations, which allows you to do the following:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=SpringockitoContextLoader.class)
public class MyTestClass {
#Autowired #ReplaceWithMock MockInterface mockObj;
// test code
}
Clearly, a whole lot prettier :) The only problem is that this context loader doesn't allow me to use #Configuration and JavaConfig for other beans (if I do, Spring complains that there are no candidates that match those autowired fields).
Do you guys know of a way to get Spring's JavaConfig and Springockito-annotations to play nice? Alternatively, is there another shorthand for creating mocks?
As a nice bonus, using Springockito and xml config, I was able to mock out concrete classes without providing autowiring candidates to its dependencies (if it had any). Is this not possible without xml?
Moving away from the now unmaintained (as of this writing) Spingockito-annotations and to Mockito, we have a way of doing this very simply:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#Mock MockInterface mockObj;
// test code
}
If you're using a real object, but would like to mock a dependency within it, for instance testing a service layer with DAO:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
// test code
}
Finally, this can also be applied to Spring-boot, but using annotation initialization within setUp() until multiple class runners are supported:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyMainSpringBootClass.class)
public class MyTestClass {
#InjectMocks RealService;
#Mock MockDAO mockDAO;
#Before
public final void setUp() throws Exception{
MockitoAnnotations.initMocks(this);
}
// test code
}
Outdated and deprecated!
Read about mocking and spying in Spring Boot 1.4
Please read also #ethesx answer,
Springockito is unmaintaned
Old answer
This is possible now to mock Spring application without any XML file with Springockito-annotations.. This solution works also with Spring Boot.
import static org.mockito.BDDMockito.*;
import org.kubek2k.springockito.annotations.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class,
loader = SpringockitoAnnotatedContextLoader.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MainControllerTest {
#Autowired
MainController mainController;
#Autowired
#ReplaceWithMock
FooService fooService;
#Test
public void shouldGetBar() {
//given
given(fooService.result("foo")).willReturn("bar");
//when
Bar bar build = fooService.getBar("foo");
//then
assertThat(bar).isNotNull();
}
}
Dependencies: org.kubek2k:springockito-annotations:1.0.9
It appears that SpringockitoContextLoader extends GenericXmlContextLoader which is described as:
Concrete implementation of AbstractGenericContextLoader that reads bean definitions from XML resources.
So you are limited to xml bean definitions at the moment.
You could write your own context loader, taking relevant parts from the SpringockitoContextLoader class. Take a look here to get started, perhaps you could extend AnnotationConfigContextLoader for example?

Categories