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.
Related
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;
I'm trying to inject a service-annotated class into a configuration class in a Spring Boot application, but it doesn't get injected (is set to null), which I assume is due to the Spring lifeycle.
Also, this service has an overloaded constructor that uses constructor injection, and I guess this is also a problem, as autowiring acts upon a default constructor. However, the service needs to be Spring-configured, so I don't think one can create a new instance in a Bean annotated method.
How can one solve this?
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private SessionService sessionService;
#Bean
public SessionService sessionService() {
return sessionService;
}
}
public interface SessionService extends BaseCacheService<Session> {
void extendExpiration(String key);
String getSessionId(String key);
}
#Service
public class SessionServiceImpl implements SessionService {
private Environment environment;
private UserService userService;
#Autowired
public SessionServiceImpl(Environment environment, UserService userService) {
this.environment = environment;
this.userService = userService;
}
}
If I exclude the #Bean method, then I get a compilation error:
Your error is the following (you are returning a null value):
#Bean
public SessionService sessionService() {
return sessionService;
}
Solution
Since your SessionServiceImpl is annotated with #Service, you can just remove the #Bean method and let spring create it. Spring already makes it available for you.
Or, If your SessionServiceImpl wasn't annotated with #Service, you would need the following :
#Bean
public SessionService sessionService() {
return new SessionService();
}
If this doesn't work, it may just be that your SessionServiceImpl is in a package not being scanned by spring (as suggested by #Miloš Milivojević)
You may add #ComponentScan to your Configuration class
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
#ComponentScan("com.package.to.sessionServiceImpl-or-higher")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
Expanding on #Alex's answer, when you annotate a method with #Bean, it tells Spring that this method will produce that type. So, you essentially told Spring to give you the null reference you already had for all Beans of type SessionService.
If you are using Annotation-based context configuration, you can Autowire any #Component Bean (not just #Service) that can be constructed without runtime parameters (e.g. has a default constructor or an Autowired Constructor). If you need to do something to create the bean (e.g. runtime configuration required), you would either create a method as #Alex suggested, or you can use getBean and pass in the Type and Constructor arguments. The former is generally preferred.
I was facing similar issue while writing an integration test class for a spring boot application. RestTemplate class and CounterService of metrics API are autowired in my service class. I could use #ContextConfiguration(Classes={RestTemplate.class}) for injecting RestTemplate to my service, but adding CounterService.class to above annotation does not help, maybe because CounterService is an interface not a concrete class, Hence I was getting "No bean of type CounterService found" issue.
Thanks to answer by Milos, I included #EnableAutoConfiguration to my integration test class, issue was resolved!
If Alex's answer does not work (removing the #Bean method), you're probably not using #EnableAutoConfiguration or your Application is not in the root-hierarchy package so it's not scanning the whole classpath. Try adding #ComponentScan("service.class.package") to your configuration (in addition to removing the sessionService method) and see if it helps.
//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
I have many spring services with this autowire:
#Autowired
private SmartCardService smartCardService;
I need a dummy class for testing and I defined this class extending the original:
#Service
public class DummySmartCardService extends SmartCardService{
...
}
How can I be sure that all autowire will take the dummy instead of original service without changing all Autowired annotation?
Thanks.
Consider using the #Primary annotation. See here
Load your DummySmartCardService bean from a test version of your application context file instead so that no changes to the code under test are necessary
#ContextConfiguration(locations = {"classpath:test-services.xml"})
Use the #Resource annotation or a #Qualifier, With #Qualifier which discriminates bean types:
#Autowired
#Qualifier("testing")
private SmartCardService smartCardService;
#Service
#Qualifier("testing")
public class DummySmartCardService extends SmartCardService{
...
}
Or with #Resource which uses by-name semantics:
#Resource("dummySmartCardService")
private SmartCardService smartCardService;
#Service("dummySmartCardService")
public class DummySmartCardService extends SmartCardService{
...
}
Theoretically you could use #Qualifier("beanName") but it is discouraged.
But it think would be better if you had a Spring profile to load only test related stubs in your tests:
#Service
#Profile("test")
public class DummySmartCardService extends SmartCardService{
...
}
#ContextConfiguration(locations = {"classpath:services.xml"})
#ActiveProfiles("test")
public class TestSuite{
#Autowired
private SmartCardService smartCardService;
}
IMHO you should take a look at Springockio for proper and rather easy mocking of Spring beans.
You can replace your bean with a mock or wrap with a Spy this way:
#ContextConfiguration(loader = SpringockitoContextLoader.class,
locations = "classpath:/context.xml")
public class SpringockitoAnnotationsMocksIntegrationTest extends
AbstractJUnit4SpringContextTests {
#ReplaceWithMock
#Autowired
private InnerBean innerBean;
#WrapWithSpy
#Autowired
private AnotherInnerBean anotherInnerBean;
....
}
This not only is a clean way (you do not need to change the code being tested by adding qualifiers or profiles) but also allows you to use the capabilities of Mockito for mocking, verifying and spying which is great.
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?