Issue with Mockito and JpaRepository - spring

I have a Spring boot project that I'm migrating to Spring boot 2.
Some of my tests that where working with the previous version are failing with the new version.
The issue is easy to reproduce.
I create 2 Controller. In the first, I inject a Service. In the second, I inject a JpaRepository.
Now I create UnitTests for both this Controller.
For the second Controller, I mock the Repository. Everything works fine.
For the first Controller, I mock the Service. The test fail.
It seems to search to inject the Repository whereas it is not used in this Controller.
I create a very simple project to reproduce this behavior :
https://github.com/YLombardi/mockito-spring-jpa-issue
Is it my test that is wrong or a bug ?

I was able to reproduce the issue from the repo url you gave. If you notice the exception when you run ControllerAUnitTests, it says
...UnsatisfiedDependencyException: Error creating bean with name 'controllerB'...
Now, why would spring try to load controllerB bean when you are running tests for ControllerA?
This is because you are using SpringRunner and default mockMvc. It loads whole spring context that means it will create beans of every component including controllerB.
To create the bean of controllerB, you need to have an instance of JpaRepo. So, just like you created mocked bean of ServiceA in ControllerAUnitTests, you need to create mocked bean for JpaRepo as well.
#MockBean
private ServiceA serviceA;
// NOTE THIS BLOCK
#MockBean
private JpaRepo jpaRepo;
#Test
public void test() throws Exception {
doReturn("a").when(serviceA).getAString();
mockMvc.perform(get("/a"))
.andDo(print())
.andExpect(status().isOk())
// Test now runs but assert fails below, that you need to fix.
.andExpect(view().name("aview"))
.andExpect(content().string("a"));
}

Related

Spring Autowire bean NullPointerException during Groovy tests but works fine during Runtime

I am using the prometheus library for getting metrics of my Spring Boot application (REST API). I am using the library io.prometheus.simpleclient:0.4.0 and I am including it in my Maven pom.xml. I am using the Counter and #Autowiring (I've tried both field and constructor injection) it to one of my own classes, like such
MyCustomMetricsClass.java
#Component
public MyCustomMetricsClass {
#Autowire
private Counter counterBean;
public void myOwnMetricsMethod() {
counterBean.inc();
// do some stuff
}
THEN, I am #Autowiring this MyCustomMetricsClass into my Service class, MyServiceClass.java, where it seems to run fine when I run my API locally using Spring Boot embedded tomcat on port 8080 (localhost:8080). I can hit endpoints and the metrics are being reported correctly at the actuator endpoint (localhost:8080/actuator/metrics). e.g.
MyServiceClass.java
public MyServiceClass {
#Autowire
private MyCustomMetricsclass myMetrics;
public void genericServiceMethod() {
myMetrics.MyOwnMetricsMethod(); // NULL POINTER EXCEPTION ONLY DURING TEST SCOPE (GROOVY)
}
The problem is, when I run mvn install, which triggers the local Groovy unit tests I have written, I keep getting a NULL POINTER EXCEPTION. With the debugger, I can debug the Groovy unit tests and see in my Service class, the myMetrics variable is NULL. But I don't understand why it works fine at runtime, also, I have annotated the MyCustomMetricsClass as a #Component annotation, so it should be a bean being scanned by Spring Component scan.
This is a multi-module project ; with the structure below
my-project (root, contains root pom.xml)
- my-api (module, contains RestController. has its own pom.xml)
- my-service (module. contains service classes, has its own pom.xml)
- my-model (module, contains all POJO/DTO model classes, has its own pom.xml)
Am I missing some dependency on my classpath? Why does it work at runtime but not during tests? (all my dependencies should have default scope) Is the autowiring broken?
Can you share the code from your unit test?
At a guess, you're using a mocking framework, maybe Mockito?
If this assumption is true, remember, your unit test won't be running up the full Spring context, so no auto-wiring will take place. You will need to inject mocks for the autowired components.
e.g.:
#RunWith(MockitoJUnitRunner.class)
public class MyServiceClassTest {
#Mock
private MyCustomMetricsClass myCustomMetricsClass;
#InjectMocks
private MyServiceClass myServiceClass;
#Test
public void shouldDoTesting() {
myServiceClass. genericServiceMethod();
verify(myMetrics).MyOwnMetricsMethod();
}
}

Are the project beans already instantiated when we try to run a junit test in spring boot

I am new to Spring and spring boot.
For my spring boot application which is a rest controller, I have some beans along with my data source.
I use my data source to create jdbc template. Now when I am in my rest controller code, I have all these beans #Autowired and they work perfectly fine.
My query is regarding the junit testing part.
When I write my test code inside src/test/java and when I execute my test class within IDE, are the beans defined in my src/main/javacode, instantiated before test case execution?
You might use the same container, or instantiate another container particularly for testing purposes, for which you'll provide a configuration of that other Spring Container separately:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:test-context.xml")
public class SomeClassTest{...}
However, you can also enable support for loading your Application Context and then use the #Autowired fields in your JUnit fixtures, which also works fine too:
#RunWith(SpringRunner.class)
public class SomeTestClass {
....
#Autowired
ApplicationContext context;
....
}
From here, you can get any bean you wish.

Spring Boot Test - Mocking A Handler Bean That Is Placed Deep In The Chain Of Responsibility

This should happen quite often:
RestController -> SomeClass -> SomeOtherClass -> YetAnotherClass and so on...
In my specific case there is a chain of responsibility which is injected to a rest controller. Each class is injected to it's previous class in the above chain.
I have implemented this with spring boot and I'm trying to test the REST resource. I want to Mock the "YetAnotherClass" so that when I send a request with MockMvc I can verify that something has happened in the mock object.
The problem is if I use #MockBean to mock YetAnotherClass then I have to inject it to SomeOtherClass. I have tried to inject it with #TestConfiguration but it seems that the Mock object injection doesn't work this way when the request is sent through MockMvc and the mock object is nested deep inside a chain such as above. (The original bean is injected not the mock one)
I know that JMockit mocks every instance of a class so it would solve my problem. But Spring boot defaults to Mockito and I prefer to avoid inconsistencies.
How can I implement such a test scenario?
I've run into a lot of annoyance using Mockito's annotation config setup when setting up Spring JUnit text fixtures.
I've found the way I like mocking beans with external integrations like this this by essentially having a separate MockObjectsConfig class with the mock objects I want using the standard Spring Context Configuration, and then import it alongside my real test config:
#Configuration
public class MockObjectsConfig {
#Bean
public YetAnotherClass yetAnotherClass() {
Mockito.mock(YetAnotherClass.class); // and add any thenReturns, answers, etc. here
}
... More mock beans...
}
Then include it in your test like so:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { MyRealConfigClass.class, MockObjectsConfig.class)
public class MyJunitTest {
#Autowired
private RestController restController;
}
You can also annotate your mock bean with #Profile and test with #ActiveProfiles if you need to prevent a conflict there.
This way your mock YetAnotherClass will get injected into your context like all your other beans -- no relying on, mixing, and fiddling around with Mockito and other library annotations.

Spock with Spring Boot and Camel: Zero interactions with detached mock

I am having some issues with testing my camel context with spring boot.
I am using spring boot 1.5.6, spock 1.1-groovy-2.4, camel 2.19.2, and camel-spring-boot-starter 2.19.2.
I am using a spock mock, and I'm using the DetachedMockFactory in a #TestConfiguration class. All of my beans use constructor injection. I am injecting a mocked #Repository into one of the processor #Components, and I am also injecting it into my test class to define interactions.
I have my test annotated with #SpringBootTest with the classes list including all Processor implementations, and all RouteBuilder extensions. I also have an '#Import' with my TestConfiguration class. I am even using constructor injection for this repository bean in my test!
But it seems that the mock that is injected into the test class is not the one that is in use. Does anyone have an idea what could be wrong? I have tried #DirtiesContext to reload the context both before and after each test, but that did not help.
Problems with DetachedMocks not behaving correctly, e.g., appearing to be the same instance, are usually caused by some framework wrapping them in proxies. For example this can be caused by #Transactional annotation in Spring, which creates a proxy to facilitate jdbc-session management. See also issue #758
For spring you can use the methods of AopUtils (jdoc). The simple way is to use AopUtils.isAopProxy to check if it is proxied by spring an then unwrap it.
public static <T> T getTargetObject(Object proxy) throws Exception {
if (AopUtils.isAopProxy(proxy)) {
return (T) ((Advised) proxy).getTargetSource().getTarget();
} else {
return (T) proxy;
}
}
And in a Test
def "sample service test"() {
given:
def sampleRepositryMock = getTargetObject(sampleRepositry)
when:
sampleService.doSomething() // simply invoke sampleRepositry.doSomething() in it
then:
1 * sampleRepositryMock.doSomething()
0 * _
}
Edit: Since Spock 1.2 there is an extension to automatically unwrap injected beans #UnwrapAopProxy.
#Inject
#UnwrapAopProxy
SampleRepositry sampleRepositryMock
If someone comes up with the same problem.
Spock added additional #UnwrapAopProxy that will do the job for you instead of the util method mentioned above. You can also drop the DetachedMockFactory
#SpringSpy
#UnwrapAopProxy
Service service

WebApplicationContext is always null in a spring boot test

My test class looks like this
#SpringBootTest(webEnvironment=WebEnvironment.MOCK)
public class sampleClassTest{
#Autowired
private WebApplicationContext wac;
}
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
In the setup method, wac is always null. From spring boot documentation, #SpringBootTest(webEnvironment=WebEnvironment.MOCK) always created a mock WebapplicaitonContext.
So I would expect it get autowired in the code above which doesn't happen.
Can someone tell me how to go about creating a webapplicationContext in this case so that it's not null like in my case ?
UPDATE
I am running spring boot tests invoking them from a class with springboot annotation.
Both test (springboottest) and calling class (springboot) application are in the same spring boot project under src/main/java.
I have nothing under src/main/test. I have done in this way because if classes from src/main/java want to call a test class then, it isn't really a test class.
Now, the problem is that I can't use runWith(SpringRunner.class) in springbootTest class. If I did that to get a mock webApplicationContext then, it gives me this error:
javax.management.InstanceAlreadyExistsException: org.springframework.boot:type=Admin,name=SpringApplication
I am not sure how to do about this.
To use #SpringBootTest you need to use Spring Framework's test runner. Annotate your test class with #RunWith(SpringRunner.class).
If someone is struggling with this issue in 2022 - please keep my defined precondions in mind. If you are using #SpringBootTest with defined port and constructor auto-wiring of the test class, the application context might be null.
It seems that the constructor dependency injection is eager and the cache aware context delegate of Spring is searching for a web application context which is no available yet. If you use field auto-wiring your test might run in a deterministic manner.
Whoever is facing this issue, make sure your spring boot starter parent version is compatible with spring cloud version in pom.xml
I was facing same issue, i resolved it by doing same.

Resources