I would like to intercept JUnit test methods. I already have interceptor that prepares context for the test. I don't want to duplicate the code in my interceptor.
Is it possible to tell SpringJUnit4ClassRunner to use myTest() instance of the test class? Something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { MyConfig.class })
public class MyTest {
#Autowired
private MyInterceptor myInterceptor;
#Autowired
private BeanFactory beanFactory;
#Bean
public MyTest myTest() {
final ProxyFactoryBean proxy = new ProxyFactoryBean();
proxy.setBeanFactory(this.beanFactory);
proxy.setTarget(this);
proxy.addAdvice(this.myInterceptor);
return (MyTest) proxy.getObject();
}
}
Related
I'm trying to verify that a mocked method (fileSenderService.sendFile) was called exactly 2 times. For whatever reason Mockito never fails the test, no matter what number of invocations are expected. I'm using it like this:
verify(mockService, times(expectedNumberOfInvocations)).sendFile(any(String.class), any(byte[].class));
No matter what value I use in the times() method, the test always passes.
The MyService looks like this:
#Service
public class MyServiceImpl implements MyService {
#Autowired
private FileSenderService fileSenderService;
public void createAndSendFiles(){
//doSomeStuff, prepare fileNames and fileContents(byte arrays)
//execute the sendFile twice
fileSenderService.sendFile(aFileName, aByteArray); //void method; mocked for testing
fileSenderService.sendFile(bFileName, bByteArray); //void method; mocked for testing
}
The test class
#RunWith(SpringRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = {Application.class, FileSenderServiceMock.class})
#ContextConfiguration
public class MyServiceTest{
#Autowired
private MyService myService;
#Autowired
FileSenderService mock;
#Test
public void shouldCreateAndSendFiles(){
myService.createAndSendFiles(); //inside this method sendFile() is called twice
verify(mock, times(999)).sendFile(any(String.class), any(byte[].class)); //THE PROBLEM - why times(999) does not fail the test?
}
}
The FileSenderService and its mock:
#Service
public class FileSenderServiceImpl implements FileSenderService {
#Override
public void sendFile(String name, byte [] content) {
//send the file
}
}
//used for testing instead of the actual FileSenderServiceImpl
public class FileSenderServiceMock {
#Bean
#Primary
public FileSenderService getFileSenderServiceMock(){
FileSenderServicemock = Mockito.mock(FileSenderService.class, Mockito.RETURNS_DEEP_STUBS);
doNothing().when(mock).sendFile(isA(String.class), isA(byte[].class));
return mock;
}
If you are using #SpringBootTest for integration test cases you don't need to define any test config classes for mocks, you can simply use #MockBean annotation to inject mock into test context
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyServiceTest{
#Autowired
private MyService myService;
#MockBean
FileSenderService fileSenderService;
#Test
public void shouldCreateAndSendFiles(){
myService.createAndSendFiles();
verify(fileSenderService, times(2)).sendFile(any(String.class), any(byte[].class));
}
}
Is it possible to inject a mock service into a prototype bean using the #Autowired constructor? I realize I could switch to setter injection but I would prefer to use the constructor if possible.
#Component
#Scope(value = "prototype")
public class Prototype {
private DependantService dependantService;
#Autowired
public Prototype(DependantService dependantService) {
this.dependantService = dependantService;
}
}
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
// How can I inject the mock service?
ctx.getBean(Prototype.class);
}
}
Turns out there is an overloaded version of the getBean method that accepts arguments. I would downvote my on question if I could.
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
Prototype p = ctx.getBean(Prototype.class, dependantService);
// Test p
}
}
If you want to speed up your unit tests, [and do true isolated unit testing,] I suggest taking a look at the #InjectMocks mockito annotation. #SpringBootTest fires up the Spring container which is pretty cumbersome.
#Controller
public class MyController {
#Inject
private Logger log;
public methodThatNeedsTesting(){
log.info("hey this was called");
}
}
#TestInstance(Lifecycle.PER_CLASS)
#ExtendWith({ MockitoExtension.class })
class MyControllerTest {
#Mock
private Logger log;
#InjectMocks
private MyController myController;
#Test
void test_methodThatNeedsTesting() throws Exception {
myController.methodThatNeedsTesting();
// myController will not throw an NPE above because the log field has been injected with a mock
}
Trying to write an integration test for a Spring application. Say i've got a class A which contains a class B object. Class B contains a class C object and I need to mock an object within this class for the integration test - any idea how i go about doing that without passing every object through as a parameter in the constructor?
e.g.
#Service
Class A {
#Autowired
private B b;
public void testA() {
B.testB();
}
}
#Service
Class B {
#Autowired
private C c;
public void testB() {
c.testC();
}
}
#Service
Class C {
//External class pulled in from dependency library
#Autowired
private RestTemplate restTemplate;
public void testC() {
restTemplate.doSomethingInOutsideWorld();
}
}
Integration test:
#RunWith(JUnitParamsRunner.class)
#SpringBootTest
public class MyIt {
#ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Mock
private RestTemplate restTemplate;
#Autowired
private A a;
#InjectMocks
private C c;
#Before
public void setup() {
initMocks(this);
}
#Test
public void test1() throws IOException {
a.testA()
}
}
Doesn't mock the RestTemplate object, it tries to hit the outside world. Any advice on how to resolve this?
Achieve this by using SpringRunner and #MockBean
#RunWith(SpringRunner.class) is used to provide a bridge between Spring Boot test features and JUnit. Whenever we are using any Spring Boot testing features in out JUnit tests, this annotation will be required.
The #SpringBootTest annotation can be used when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests.
Annotation that can be used to add mocks to a Spring ApplicationContext. Can be used as a class level annotation or on fields in either #Configuration classes, or test classes that are #RunWith the SpringRunner.
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyIt {
#MockBean
private RestTemplate restTemplate;
#Autowired
private A a;
#Before
public void setup() {
initMocks(this);
}
#Test
public void test1() throws IOException {
given(this.restTemplate.doSomethingInOutsideWorld()).willReturn(custom object);
a.testA()
}
}
Here is my class under test:
KafkaProcessorApplication
#EnableBinding(Processor.class)
#EnableConfigurationProperties(KafkaProperties.class)
public class KafkaProcessorApplication {
#Autowired
private Processor processor;
#Autowired
private KafkaProperties kafkaProperties;
private KafkaTemplate<String, String> kafkaTemplate;
#Autowired
KafkaProcessorApplication(SenderConfig senderConfig) {
this.kafkaTemplate = senderConfig.kafkaTemplate();
}
Here, SenderConfig is a just a simple config class with the method kafkaTemplate() creating a new instance of KafkaTemplate.
SenderConfig
#Configuration
public class SenderConfig {
#Autowired
KafkaProperties kafkaProperties;
public ProducerFactory<String, String> producerFactory() {
return new DefaultKafkaProducerFactory<>(new HashMap());
}
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(ProducerFactory()));
}
}
Here is the test class:
KafkaTestClass
#SpringBootTest
#ActiveProfiles("test")
#ContextConfiguration(classes = {SenderConfig.class, KafkaProcessorApplication.class})
#TestPropertySource(locations = "classpath:test-resources.properties")
#RunWith(SpringRunner.class)
public class KafkaProcessorApplicationTest {
#Autowired
private Processor processor;
#Mock
private SenderConfig senderConfig;
#Mock
private KafkaProperties kafkaProperties = new KafkaProperties();
#Mock private KafkaTemplate mockKafka;
#Autowired
#InjectMocks
private KafkaProcessorApplication app;
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn("ServerConfig").when(kafkaProperties).getServersConfig();
when(senderConfig.kafkaTemplate()).thenReturn(kafkaTemplate);
}
I want to mock kafkaTemplate. But, its instantiation is in constructor which is being executed even before the #Before is executed, where the logic of mocking the method is written.
Just curious why is the constructor being executed first, and also, how can I mock the method if this is the case?
What could be the approaches of mocking the kafkaTemplate, without using Powermock and without modifying the class under test as I can not change it?
When you use #SpringBootTest, the Spring dependency tree is resolved before the #Before method has a chance to execute. This includes constructing the KafkaProcessorApplication bean and its dependencies. This is why the constructor runs before #Before.
What you want is Spring's #MockBean to create and inject a mock bean in the application context.
This question has a great write-up of how you can use this: Difference between #Mock, #MockBean and Mockito.mock()
update
Now I see. The problem is that the KafkaProcessorApplication accesses the mock in its constructor before you can configure it.
This can be solved with a separate test Spring configuration that will return a configured SenderConfig mock bean as described here: Testing spring bean with post construct
I'm using Mockito to test my Spring project, but the #InjectMocks seems not working in injecting a mocked service into another Spring service(bean).
Here is my Spring service that I want to test:
#Service
public class CreateMailboxService {
#Autowired UserInfoService mUserInfoService; // this should be mocked
#Autowired LogicService mLogicService; // this should be autowired by Spring
public void createMailbox() {
// do mething
System.out.println("test 2: " + mUserInfoService.getData());
}
}
And below is the service that I want to mock:
#Service
public class UserInfoService {
public String getData() {
return "original text";
}
}
My test code is here:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
#Mock
UserInfoService mUserInfoService;
#InjectMocks
#Autowired
CreateMailboxService mCreateMailboxService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void deleteWithPermission() {
when(mUserInfoService.getData()).thenReturn("mocked text");
System.out.println("test 1: " + mUserInfoService.getData());
mCreateMailboxService.createMailbox();
}
}
but the result would like
test 1: mocked text
test 2: original text // I want this be "mocked text", too
it seems that the CreateMailboxService didn't get the mocked UserInfoService but using Spring's autowired bean.
Why is my #InjectMocks not working?
In my case, I had a similar issue when I worked with JUnit5
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
...
#InjectMocks
MyService underTest;
#Test
void myMethodTest() {
...
}
underTest was null.
The cause of the problem was that I used #Test from JUnit4 package import org.junit.Test; instead JUnit5 import org.junit.jupiter.api.Test;
For those who stumbles on this thread and are running with JUnit 5 you need to replace
#RunWith(SpringJUnit4ClassRunner.class)
with
#ExtendWith(MockitoExtension.class)
#RunWith(JUnitPlatform.class)
Further reading here. Unfortunately there is no hint when executing the test cases with JUnit 5 using the old annotation.
You can create package level setter for mUserInfoService in CreateMailboxService class.
#Service
public class CreateMailboxService {
#Autowired UserInfoService mUserInfoService; // this should be mocked
#Autowired LogicService mLogicService; // this should be autowired by Spring
public void createMailbox() {
// do mething
System.out.println("test 2: " + mUserInfoService.getData());
}
void setUserInfoService(UserInfoService mUserInfoService) {
this.mUserInfoService = mUserInfoService;
}
}
Then, you can inject that mock in the test using the setter.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
#Mock
UserInfoService mUserInfoService;
CreateMailboxService mCreateMailboxService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mCreateMailboxService = new CreateMailboxService();
mCreateMailboxService.setUserInfoService(mUserInfoService);
}
...
}
This way you can avoid problems with #InjectMocks and Spring annotations.
If you are trying to use the #Mock annotation for a test that relies directly on Spring injection, you may need to replace #Mock with #MockBean #Inject (both annotations), and #InjectMocks with #Inject. Using your example:
#MockBean
#Inject
UserInfoService mUserInfoService;
#Inject
CreateMailboxService mCreateMailboxService;
I had a pretty similar situation. I am writing it down just in case any reader is going through the same. In my case I found that the problem was that I was setting my injected variable as final in the local class.
Following your example, I had things like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/root-context.xml" })
public class CreateMailboxServiceMockTest {
#Mock
UserInfoService mUserInfoService;
#InjectMocks
CreateMailboxService mCreateMailboxService = new CreateMailboxService(mUserInfoService);
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void deleteWithPermission() {
...
}
}
But in this class I had it like this:
#Service
public class CreateMailboxService {
private final UserInfoService mUserInfoService; // it is NOT injecting Mocks just because it is final! (all ok with private)
private final LogicService mLogicService; // it is NOT injecting Mocks just because it is final! (all ok with private)
#Autowired
public CreateMailboxService(UserInfoService mUserInfoService, LogicService mLogicService) {
this.mUserInfoService = mUserInfoService;
this.mLogicService = mLogicService;
}
public void createMailbox() {
...
}
}
Just deleting the final condition, #InjectMocks problem was solved.
For those who are running with JUnit 5 you need to replace the #RunWith(SpringJUnit4ClassRunner.class) with #ExtendWith(MockitoExtension.class).
For further reading take a look here.
there is no need of #Autowired annotation when you inject in the test class. And use the mock for the method to get your mocked response as the way you did for UserInfoService.That will be something like below.
Mockito.when(mCreateMailboxService. getData()).thenReturn("my response");
You can use MockitoJUnitRunner to mock in unit tests.
Use #Mock annotations over classes whose behavior you want to mock.
Use #InjectMocks over the class you are testing.
Its a bad practice to use new and initialize classes (better to go for dependency injection) or to introduce setters for your injections. Using setter injection to set dependencies only for tests is wrong as production code should never be altered for tests.
#RunWith(MockitoJUnitRunner.class)
public class CreateMailboxServiceMockTest {
#Mock
UserInfoService mUserInfoService;
#InjectMocks
CreateMailboxService mCreateMailboxService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
...
}