I am trying to write a test for this Java SpringBoot's class:
https://github.com/callistaenterprise/blog-microservices/blob/master/microservices/composite/product-composite-service/src/main/java/se/callista/microservices/composite/product/service/ProductCompositeIntegration.java
Specifically, I am trying to "mock" this method call:
URI uri = util.getServiceUrl("product");
I figured out I should "mock" the ServiceUtils object in order to do this. I tried this using the #Mock and #InjectMocks annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ProductCompositeServiceApplication.class)
public class ProductCompositeIntegrationTest {
#InjectMocks
#Autowired
private ProductCompositeIntegration productIntegration;
#Autowired
private RestTemplate restTemplate;
#Mock
private ServiceUtils util;
private MockRestServiceServer mockServer;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void myTest() {
Mockito.when(util.getServiceUrl("product")).thenReturn(URI.create("http://localhost:8080/test"));
ResponseEntity<Iterable<Product>> products = productIntegration.getAllProducts();
}
}
But this way it still calls the original ServiceUtils object, and not the "mocked" one. Also tried without the #Autowired annotation at the ProductCompositeIntegration, but this results in a NullPointerException.
What am I doing wrong?
My main class looks like this:
#SpringBootApplication
#EnableCircuitBreaker
#EnableDiscoveryClient
public class ProductCompositeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductCompositeServiceApplication.class, args);
}
}
The ServiceUtils object that I am trying to mock is specified in a class, annotated with Spring's #Component annotation to inject it into the other classes using #Autowired.
After a lot of trial and error I managed to solve this problem.
I dropped the
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ProductCompositeServiceApplication.class)
annotations aboved the test class.
I marked the class that I was testing with #InjectMocks and the dependencies with #Mock:
public class ProductCompositeIntegrationTest {
#InjectMocks
private ProductCompositeIntegration productIntegration;
#Mock
private ServiceUtils util;
private MockRestServiceServer mockServer;
private RestTemplate restTemplate = new RestTemplate();
#Before
public void init() {
MockitoAnnotations.initMocks(this);
mockServer = MockRestServiceServer.createServer(restTemplate);
productIntegration.setRestTemplate(restTemplate);
}
#Test
public void someTests() {
when(util.getServiceUrl("product")).thenReturn(URI.create("http://localhost:8080/test"));
//Test code...
}
}
I'm not sure if this is the best approach ("the Spring way"), but this worked for me.
This article made it all clear to me: http://rdafbn.blogspot.be/2014/01/testing-spring-components-with-mockito.html
You have to write a FactoryBean like
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private Class<T> classToBeMocked;
public MockitoFactoryBean(Class<T> classToBeMocked) {
this.classToBeMocked = classToBeMocked;
}
#Override
public T getObject() throws Exception {
return Mockito.mock(classToBeMocked);
}
#Override
public Class<?> getObjectType() {
return classToBeMocked;
}
#Override
public boolean isSingleton() {
return true;
}
}
In your test-context.xml you have to add the following lines.
<bean id="serviceUtilMock" class="MockitoFactoryBean">
<constructor-arg value="your.package.ServiceUtil" />
</bean>
If you don't use XML configuration, then you have to add the equivalent to above in your Java configuration.
Related
Is it possible to write unit test using Junit 5 mockito for retryable annotations?
I am having a service interface which has only one method, which downloads the file from remote url
#service
interface downloadpdf{
#Retryable(value = { FileNotFoundException.class, HttpClientErrorException.class }, maxAttempts = 5, backoff = #Backoff(delay = 1000))
public string downloadpdffile(string remoteurl, string pdfname);
}
I have tried referring sites and found using Spring4JunitRunner implementation to test retry. Got confused with implementation. Is it possible to write unit test using Junit 5 mockito for retryable annotations?. Could you please elaborate on the solution here?
You need to use #SpringJUnitConfig (which is the equivalent of the JUnit4 runner). Or #SpringBootTest as you are using Boot.
#Retryable only works with beans managed by Spring - it wraps the bean in a proxy.
#SpringBootApplication
#EnableRetry
public class So71849077Application {
public static void main(String[] args) {
SpringApplication.run(So71849077Application.class, args);
}
}
#Component
class RetryableClass {
private SomeService service;
void setService(SomeService service) {
this.service = service;
}
#Retryable
void retryableMethod(String in) {
service.callme();
throw new RuntimeException();
}
#Recover
void recover(Exception ex, String in) {
service.failed();
}
}
interface SomeService {
void callme();
void failed();
}
#SpringBootTest
class So71849077ApplicationTests {
#MockBean
SomeService service;
#Test
void testRetry(#Autowired RetryableClass retryable) {
SomeService service = mock(SomeService.class);
retryable.setService(service);
retryable.retryableMethod("foo");
verify(service, times(3)).callme();
verify(service).failed();
}
}
I was also trying to implement this using Junit5.
Tried various options but that didn't help. Then after googling for few hours, got the following link and it helped to succeed.
https://doctorjw.wordpress.com/2022/04/29/spring-testing-a-single-bean-in-junit-5-springextension/
Reference code below, for detailed explanation, please refer the blog.
#Component
public class MyClass {
private ObjectMapper objectMapper;
private RestTemplate restTemplate;
#Value("${testValue:5}")
private int value;
#Retryable(....)
public void doStuff() throws SomeException {
...
}
}
What I’ve discovered is, if I declare my test class this way:
#ExtendWith( SpringExtension.class )
#Import( { MyClass.class, ObjectMapper.class } )
#EnableRetry
public class MyClassTest {
#Autowired
private MyClass myClass;
#MockBean
private RestTemplate restTemplate;
#Autowired
private ObjectMapper objectMapper;
#BeforeEach
public void setup() {
// If we are going to jack with the object configuration,
// we need to do so on the actual object, not the Spring proxy.
// So, use AopTestUtils to get around the proxy to the actual obj.
TestingUtils.setFieldValue( AopTestUtils.getTargetObject( myClass ), "value", 10 );
}
}
You will notice the inclusion of 1 other class, TestingUtils.class. This class looks like:
public class TestingUtils {
public static void setFieldValue( Object object, String fieldName, Object value ) {
Field field = ReflectionUtils.findField( object.getClass(), fieldName );
ReflectionUtils.makeAccessible( field );
ReflectionUtils.setField( field, object, value );
}
}
All credits goes to the author of the blog.
I'm making test code in spring boot.
But, my test code doesn't save the data using #Before method.
If i request to '/v1/stay/, it return empty array...
Please can you explain what is wrong with my code?
Here is my test code.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class StayControllerTest {
#MockBean
private StayService stayService;
#Autowired
private MockMvc mockMvc;
// givenStay method is the method generating dummy data
#Before
public void before() {
stayService.save(givenStay1());
stayService.save(givenStay2());
stayService.save(givenStay3());
stayService.save(givenStay4());
stayService.save(givenStay5());
}
#Test
#Transactional
void showStayList() throws Exception {
List<StayReq> original = new ArrayList<>();
original.add(givenStay1());
original.add(givenStay2());
original.add(givenStay3());
original.add(givenStay4());
original.add(givenStay5());
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/v1/stay")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println(result.getResponse());
}
}
And below code blocks are my StayController and StayService
#RestController
#ApiV1
#RequiredArgsConstructor
public class StayController {
private final StayService stayService;
private final ApiService apiService;
#GetMapping("/stay")
public ResponseEntity<Response> stayList() {
return apiService.okResponse(stayService.getList());
}
}
#Service
#RequiredArgsConstructor
public class StayService {
private final StayRepository stayRepository;
private final RoomRepository roomRepository;
public List<StayRes> getList() {
return stayRepository.findAll().stream().map(StayRes::new).collect(Collectors.toList());
}
#Transactional
public void save(StayReq stayReq) {
stayRepository.save(stayReq.toEntity());
}
}
You injected a mock, not a 'real' service. If you want to use a 'real' service - you need to replace #MockBean annotation with #Autowired annotation.
Or alternatively - you can configure mock in the test method to return some predefined data.
I have a service that has a DataProvider which I want to mock.
Problem: the service uses the data provider in #PostConstruct. But when I use #MockBean, the mocked values are not jet present in #PostConstruct.
What could I do?
#Service
public class MyService {
private List<Object> data;
#Autowired
private DataProvider dataProvider;
#PostConstruct
public void initData() {
data = dataProvider.getData();
}
public void run() {
System.out.println(data); //always null in tests
}
}
#SpringBootTest
public class Test {
#MockBean
private DataProvider dataProvider;
#Test
public void test() {
when(dataProvider.getData()).thenReturn(mockedObjects);
//dataProvider.init(); //this fixes it, but feels wrong
service.run();
}
}
IMHO unit testing MyService would be a better solution for this particular scenario (and I wouldn't feel wrong about calling initService manually in that case), but if you insist...
You could simply override the DataProvider bean definition for this particular test and mock it beforehand, sth like:
#SpringBootTest(classes = {MyApplication.class, Test.TestContext.class})
public class Test {
#Test
public void test() {
service.run();
}
#Configuration
static class TestContext {
#Primary
public DataProvider dataProvider() {
var result = Mockito.mock(DataProvider.class);
when(result.getData()).thenReturn(mockedObjects);
return result;
}
}
}
You might need to set spring.main.allow-bean-definition-overriding to true for the above to work.
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()
}
}
I want to replace an autowired class of a service in my spring boot app with a mocked implementation of that class that I created specifically for testing.
I chose to create this mocked implementation because the behaviour of this class is too complicated to mock using mockito as it requires multiple other mocks itself.
I am not able to work out how to inject this mocked implementation into the service.
Here is a minimal example of the situation:
#Service
public class ServiceIWantToTestImpl implements ServiceIWantToTest{
#Autowired
ComplicatedDependency complicatedDependency;
#Override
public void methodUsingDependency(){
String string = complicatedDependency.doSomething();
System.out.println(string);
}
}
public class MockComplicatedDependency implements ComplicatedDepencency{
public MockComplicatedDependency(...){
// Inject other mocked objects into this mock
}
public String doSomthing(){
// This would be a mocked version of this function for testing
return "test";
}
}
#RunWith(MockitoJUnitRunner.class)
public class TestingTheService(){
#InjectMock
private static ServiceIWantToTest serviceIWantToTest = new ServiceIWantToTestImpl();
#Mock
ComplicatedDependency mockComplicatedDependency;
#BeforeClass
public static void init(){
mockComplicatedDependency = new MockComplicatedDependency(...);
}
#Test
public void testAttempt(){
serviceIWantToTest.methodUsingDependency(); // This method calls complicatedDependency.doSomething() which does not run the mocked version in MockComplicatedDependency which I wanted to inject, and would always return null instead of the "test" string I put in this example.
}
}
Do you have to use Mockito annotations to setup dependencies for the class under test?
If that is not the main constraint why not just do the plain simple setup and introduce a constructor or a setter in ServiceIWantToTestImpl class for the ComplicatedDependency field and set the dependency in your test setup directly to whatever impl of ComplicatedDependency you like e.g.:
#Service
public class ServiceIWantToTestImpl implements ServiceIWantToTest {
#Autowired
ComplicatedDependency complicatedDependency;
public ServiceIWantToTestImpl() {
}
public ServiceIWantToTestImpl(ComplicatedDependency complicatedDependency) {
this.complicatedDependency = complicatedDependency;
}
#Override
public void methodUsingDependency(){
String string = complicatedDependency.doSomething();
System.out.println(string);
}
}
public class TestingTheService {
private static ServiceIWantToTestImpl serviceIWantToTest;
#BeforeClass
public static void init(){
serviceIWantToTest = new ServiceIWantToTestImpl(new MockComplicatedDependency());
}
#Test
public void testAttempt() {
serviceIWantToTest.methodUsingDependency();
}
}
That is one way.
To make it work with Mockito, You could to use #Spy instead of #Mock like this:
#RunWith(MockitoJUnitRunner.class)
public class TestingTheService {
#InjectMocks
private static ServiceIWantToTestImpl serviceIWantToTest = new ServiceIWantToTestImpl();
#Spy
private static ComplicatedDependency complicatedDependency = new MockComplicatedDependency();
#BeforeClass
public static void init() {
}
#Test
public void testAttempt() {
serviceIWantToTest.methodUsingDependency();
}
}
Though this is a bit of a hack. I strongly recommend that you read the JavaDoc of the #Spy annotation and make sure it's expected use is what you really need for your test.