MockBean doesn't work in Spring boot integration test - java

I have Spring Integration test where I'm trying to Mock some of my Beans. For some reason although I Mocked them they are NULL. Here is code snippet:
The Bean which I want to Mock
#Component
public class MockWS {
public String callSoapClient() throws JAXBException{
return "CallSoapCl";
}
}
The class where the Bean is used
public class SmDpES2PortImpl implements ES2SmDp {
#Autowired
private MockWS mock;
#Override
public void es2DownloadProfile(ES2DownloadProfileRequest parameters) {
try {
LOG.info("\n\n\n TEST BEAN: " + mock.callSoapClient() + "\n\n");
}
}
}
Spring boot integration test where the Bean has been mocked
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ES2SmDpApplicationTests {
#MockBean(name="mockWS")
MockWS mockService;
#Test
public void test1Es2DownloadProfile_Sucess() throws MalformedURLException, JAXBException, SOAPException {
when(mockService.callSoapClient()).thenReturn("CallMockCLient");
}
}
Output from the build execution: TEST BEAN: null

In my case the following combination of annotations worked:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { ControllerThatIAmTesting.class })
#AutoConfigureMockMvc(addFilters = false) // if using MockMvc object
But I had to declare explicitly both Autowired objects that I use in the ControllerThatIAmTesting in the test class with #MockBean annotation - otherwise Spring would complain that it cannot find suitable implementation - incidentally both my interfaces and their implementations are in the same corresponding packages
Also, using #WebMvcTest instead of #SpringBootTest (other suggest it as more specific scenario) resulted in Spring failing to find and initialize some other #Autowired dependencies from my #Configuration classes.
Related posts post1 post2 post3

You should mock an interface, not a class. Also, SmDpES2PortImpl must be a Spring bean. Try the following:
Interface:
public interface IMockWS {
public String callSoapClient() throws JAXBException;
}
Component class:
#Component
public class MockWS implements IMockWS {
#Override
public String callSoapClient() throws JAXBException{
return "CallSoapCl";
}
}
Service class:
#Service //Also #Component is a good alternative
public class SmDpES2PortImpl implements ES2SmDp {
#Autowired
private IMockWS mock; //Notice that you are wiring an interface
#Override
public void es2DownloadProfile(ES2DownloadProfileRequest parameters) {
try {
LOG.info("\n\n\n TEST BEAN: " + mock.callSoapClient() + "\n\n");
}
}
}
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ES2SmDpApplicationTests {
#MockBean
IMockWS mockService; //Again, you are mocking the interface, not the implementing class
#Test
public void test1Es2DownloadProfile_Sucess() throws MalformedURLException, JAXBException, SOAPException {
when(mockService.callSoapClient()).thenReturn("CallMockCLient");
}
}

Related

Retryable annotation - Junit5 - Mockito - is it possible

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.

Mock embedded objects in integration test

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()
}
}

Testing using spring boot throws null pointer exception

I am new to spring boot and i am trying to test a very simple class. But when i run the testMe() below I get exception below
java.lang.NullPointerException
at MyTest.testMe(MyTest.java:25)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
My understanding is when the context is loaded all the beans are initialized and object HelloWorld is created and are autowired in MyTest call. But helloWorld object is null at line helloWorld.printHelloWorld();
I need assistance here to understand what is missing.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = {AppConfigTest.class})
public class MyTest {
#Mock
#Autowired
private Message myMessage;
#Autowired
private HelloWorld helloWorld;
#Test
public void testMe(){
helloWorld.printHelloWorld();
}
}
#Configuration
public class AppConfigTest {
#Bean
public HelloWorld helloWorld() {
return new HelloWorldImpl();
}
#Bean
public Message getMessage(){
return new Message("Hello");
}
}
public interface HelloWorld {
void printHelloWorld();
}
public class HelloWorldImpl implements HelloWorld {
#Autowired
Message myMessage;
#Override
public void printHelloWorld() {
System.out.println("Hello : " + myMessage.msg);
}
}
public class Message {
String msg;
Message(String message){
this.msg = message;
}
}
You're running your tests with a runner that's not Spring-aware, so no wiring is happening. Look at the Spring Boot testing documentation, all their examples use #RunWith(SpringRunner.class). To mock a bean, annotate it with #MockBean, not #Mock. Make sure that the spring-boot-starter-test is included in your POM.

NullPointerException: Junit Spring annotation configuration

I am learning Spring framework and using this reference,
I have a UkranianSongs class
import javax.annotation.Nonnull;
public class UkrainianSongs implements CompactDisk{
#Override
#Nonnull
public String getTitle(){
return "Ukranian Songs";
}
#Override
#Nonnull
public String getArtist(){
return "Skriabin";
}
}
I am creating a bean in CDPlayerConfig class
#Configuration
#ComponentScan
public class CDPlayerConfig {
#Bean
public CompactDisk anUkranianDisk(){
return new UkrainianSongs();
}
}
And I am autowiring compact disk class in another class
#Component
public class CompactDiskBox {
#Autowired
public CompactDisk anUkrainianDisk;
}
I wrote a JUnit test where I am trying to assert anUkranianDisk bean was initialized correctly and is not null
#RunWith(JUnit4.class)
#ContextConfiguration(classes = CDPlayerConfig.class)
public class CompactDiskBoxTest {
#Autowired(required = true)
public CompactDiskBox compactDiskBox;
#Test
public void testUkranianDisk(){
assertNotNull(compactDiskBox.anUkrainianDisk);
}
}
This throws a NullPointerException as compactDiskBox is null which means it couldn't initialize the bean right. Am I missing something here?
In order to be able to use dependency injection in your unit/integration tests, you have to set up the Spring TestContext Framework. In order to do this you should run your tests with SpringJUnit4ClassRunner.class or SpringRunner.class. SpringRunner.class is just an alias for SpringJUnit4ClassRunner.class.

Mock object method call using Spring Boot and Mockito

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.

Categories

Resources