Spring bean autowire for testing - java

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.

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.

How to properly inject #Autowired between Spring Boot classes?

I have a classA which implements an interfaceA, with a methodA, then I have a classB in which I call classA with an #Autowired to be able to use methodA, but it gives me a warning that I must create a method for classA. Why is this happening? Doesn't #Autowired work like this in this case? Should I just instantiate classA? Thank you very much for your answers.
ClassA
#RequiredArgsConstructor
public class RepositoryImpl implements IRepository {
#Autowired
private final TransactionDataMapper transactionDataMapper;
#Autowired
private SpringDataColminvoice springDataColminvoice;
#Override
public <S extends TransactionDto> S save(S s) {
Colm colm = transactionDataMapper.toEntity(s);
//methodA
springDataColminvoice.save(colm);
return null;
}
}
InterfaceA
public interface IRepository extends IRepository<TransactionDto, Integer> {}
ClassB
#Service
#RequiredArgsConstructor
public class ServiceImpl implements IInvoiceService {
#Autowired
private RepositoryImpl repositoryImpl;
#Override
public void save(CMessage cMessage) throws HandlerException {
try {
TransactionDto transactionDto = cMessage.getTransaction();
// methodA
repositoryImpl.save(transactionDto);
} catch (Exception e) {
throw new HandlerException(e.getMessage());
}
}
}
Exception
Action:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field RepositoryImpl in com.st.ms.yyu.d.binvoce.infraestructure.rest.spring.services.impl.InvoiceServiceImpl required a bean of type 'com.st.ms.yyu.d.binvoce.infraestructure.db.springdata.repository.impl.ServiceImpl' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.st.ms.yyu.d.binvoce.infraestructure.db.springdata.repository.impl.RepositoryImpl' in your configuration.
(posting this as an answer since I do not have enough reputation to comment)
As others have pointed out already, a code sample would help tremendously.
That being said, though, it sounds like you're missing implementation for "ClassA".
If you have an interface that "ClassA" implements, you have to implement the interface's methods in "ClassA" before you can use them.
I assume your code currently looks somewhat like this?
public interface InterfaceA {
void methodA();
}
public class ClassA implements InterfaceA {
}
public class ClassB {
#Autowired
ClassA classA; // Cannot use {#link InterfaceA#methodA} because the class does not implement the function
}
If this is your code, make sure you add an implementation for your "methodA()" function in "ClassA". Somewhat like so:
public class ClassA implements InterfaceA {
#Override
public void methodA() {
}
}
Additionally, in order to autowire in Spring (Boot), you need to ensure that the class you'd like to autowire is marked as such. You can autowire beans.
To make "ClassA" in the example eligible for autowiring, make sure to instantiate it either as:
A bean (using the #Bean annotation).
A component (using the #Component annotation).
A service (using the #Service annotation).
Any of the other annotations that may match your use case the best.
In our example, this would look somewhat like this:
#Component // Or #Service / whatever you may need
public class ClassA implements InterfaceA {
#Override
public void methodA() {
}
}
Hope you've found any of this helpful. All the best!
-T
As what I have understood, #Autowire means injecting the value/instance of the specific property where you put the annotation #Autowire. In this case, #Autowire only happens when there is defined/created Bean within your basePackage of your Spring Boot project that can match it, i.e. where your #Autowire referred to (meaning there is no conflict issue like ambiguity, etc. and the DataType(Class) can be implicitly casted). In your example, first you treat the IRepository and/or RepositoryImpl as Repository without using the #Repository annotation to inform the Spring Boot default configuration that this is a Repository bean. As you didn't put the POM.xml or posted the related code, I presumed you are creating your own repository class. I think it's much better to post your dependencies here.
But as what others pointed out. You need to create a bean that can match the #Autowired you've put on TransactDataManager & SpringDataColminvoice. You need also to inform the Spring Boot or register it that your class A is a Bean by annotating
#Bean - defining a regular bean,
#Component - a Component in the Project,
#Service - a Service in the Project,
#Repository - a Repository (if you're using Spring JPA), etc.
#<Other Annotations depending of what other Spring Project/Dependencies your using>
Since newer versions of Spring is moving to annotation based from XML mapping, we need to put proper annotation for each class/object that we want to be auto injected/instantiated from #Autowired using the above sample annotations depending on the role/purpose of your class/object is.
I suggest if you're not sure. Then create a typical bean using common annotation #Bean. So your class A might be
#Component //Insert proper Annotation for your class if necessary. This is just a sample
#RequiredArgsConstructor
public class RepositoryImpl implements IRepository {
#Autowired
private final TransactionDataMapper transactionDataMapper;
#Autowired
private SpringDataColminvoice
springDataColminvoice;//segunda
#Override
public <S extends TransactionDto> S save(S s) {
//Some implementation
}
#Bean
private TransactionDataMapper getTransactionDataMapper(<Some parameters if needed>){
return <an instance for TransactionDataManager>;
}
#Bean
private SpringDataColminvoice getSpringDataColmInvoice(<Some parameters if needed>){
return <an instance for SpringDataColminvoice>;
}
}
Note that 2 beans definition are optional if there are already a Beans define on outside class or if it was marked by other annotation like #Service, #Component or other proper annotations and the other one bean is just a reference parameter for the other bean in order to properly instantiated.
In your class B is the following:
public class ClassB {
#Autowired
ClassA classA;
/*Note: ignore this Bean definition if Class A is annotated with #Component,
#Service, or other proper #Annotation for Class A.
*/
#Bean
private ClassA getClassA(<Some Parameters if Needed>){
return <Instance of Class A>
}
}
Take note that, you don't need to put a Bean definition inside the Class B if you put a proper annotation for your Class A like #Component, #Service, etc.

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;

How to Autowire a Spring-annotated service class in a #Configuration class?

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.

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