Mockito spies - verify() calls the method instead of checking - spring

I'm trying to use Springockito and spies to verify that calls were made/not made on a service method during an end-to-end test. I'm autowiring the service that the process will also get, and spy on it. Although myService instance is instrumented, verify() does not verify previous calls, but makes a call to the original method and passes a null parameter. Why is this?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = PatchedSpringockitoContextLoader.class, locations = {
"classpath:/config.xml"
})
...
#Autowired
#WrapWithSpy
private MyService myService;
...
#Before
public void setup() {
initMocks(this);
...
}
...
#Test
public void test() {
// run the process that may or may not call the service
verify(myService, never()).myMethod(any(MyParam.class));
}

What might be happening here is that your spied object uses annotations (e.g #Transactional) that requires Spring to add an AOP proxy around your spy, which causes Mockito to malfunction.
I've had the same issue as yours although I do not use Spock, and I solved it by getting a reference to the proxied mock or spy from the Spring proxy.
Check out the suggested hack in this GitHub issue report.
I am not using Spring Boot, so I wrapped the workaround code in a #BeforeClass method.

Related

MeterRegistry counter test case failing

I have implemented Micrometer Prometheus counter in my service by injecting MeterRegistry and incrementing the count as shown below, and I have written a test case as well, but when I am running the test case, I am getting:
"java.lang.NullPointerException: Cannot invoke
"io.micrometer.core.instrument.MeterRegistry.counter(String,
String[])" because "this.meterRegistry" is null".
Service file:
#Autowired
private MeterRegistry meterRegistry;
public void counterIncrement() {
meterRegistry.counter("test_count").increment();
}
Test case file:
#MockBean
private MeterRegistry registry;
#Test
void testCounter() {
// invoking counterIncrement();
}
How do you create your class under test?
Since the registry is never instantiated, something seems up with how you setup your test.
Check that you are using the #MockBean in the correct way. This will replace the bean in the application context and if you do not spin up a spring context in your test, it will not work. See this post for more info.
A different approach would be to use #Mock and inject the registry in the constructor, example:
#ExtendWith(MockitoExtension.class)
public class MyServiceTest {
#Mock
private MeterRegistry registry;
private MyService myService;
#BeforeEach
void setup() {
myService = new MyService(registry);
}
#Test
void testCounter() {
var counter = mock(Counter.class);
given(registry.counter(any(String.class))).willReturn(counter);
myService.counterIncrement();
}
You can test metrics without Mockito using SimpleMeterRegistry
#Test
void testCounter() {
var meterRegistry = new SimpleMeterRegistry();
Metrics.addRegistry(meterRegistry);
// invoke counterIncrement();
var actual = meterRegistry.counter("test_count").count();
assertEquals(1.0d, actual);
}
Depending on which junit version you are using you need to add the annotation to your test class. Junit 5: #ExtendWith(MockitoExtension.class) or for Junit 4: #RunWith(MockitoJUnitRunner.class)
Depending on the test and the service there are several ways to deal with the missing MeterRegistry.
If you use a spring context in your test, try to use a test configuration to create the MeterRegistry bean.
If your test uses some Mock framework, you could mock the MeterRegistry as suggested by by #Hans-Christian.
If you simply make the member meterRegistry non-private. You could set it to a SimpleMeterRegistry in some setup method, anotated with #BeforeEach as suggested by #checketts in the comments.
If mocking the meter registry gets complicated, you could easily build and use some factory that provides the registry and mock this factory. A very easy factory will do, e.g. a spring #Component with an autowired MeterRegistry and some public getter for the factory.
You could use the factory method pattern as described in wikipedia to get the MeterRegistry, overwrite the factory method in a subclass of your service and use this subclass in the test. (Note that the gang of four did use a static factory method, you'll need a non-static method.)
I favour solution 3 but would use solution 1 whenever appropriate. I've added solutions 4 and 5 just because there might be some additional reasons and special cases that make these solutions a good choice. If so, I prefer 4 over 5.

Unit test - Problems testing #Retryable and #Recover

I have a component that's using #Retryable annotation and another service using that component. So I'm trying to test that the component using #Retryable annotation is actually retrying.
I've tried every solution there is on the web right now but nothing worked for me. I'm trying to create a unit test for this and not integration test. So far I've managed to get to the exception that's supposed to be thrown and #Retryable wasn't even retrying, the method just threw the exception and thats it.
This is the component using Retryable annotation:
#Component
public class OurComponent {
#Retryable(maxAttempts = 10,
backoff = #Backoff(delay = 2000),
value = {someException.class}
)
public void someMethod(SomeObject someObject) throws someException {
Object createObject = anotherMethod(someObject); //this method throws someException
...
}
}
And the service using this ourComponent:
#Service
public class someService {
private final OurComponent ourComponent;
public SomeService(OurComponent ourComponent) {
this.ourComponent = ourComponent;
}
...
public void methodUsingComponent() {
SomeObject someObject = new SomeObject(args);
ourComponent.someMethod(someObject);
}
}
Now I've tried to #InjectMocks and #MockBean this service and component but it still didn't work. Is it even possible to test #Retryable annotation without doing integration test?
If you use a unit test that doesn't use spring at all, you won't be able to test it easily.
This is due to the fact that annotations like this are recognized by spring and the corresponding bean is wrapped with a runtime-generated proxy that implements the "retry" logic.
Now, if you don't have spring who triggers all this mechanism, this #Retryable annotation is basically useless, mockito doesn't know anything about, so is Junit.
You could try to create a proxy like this manually (check what logic spring-retry invokes) but it looks to be an overkill. And frankly speaking, it doesn't give you anything. Unit test should check the functionality of your code and not the logic behind spring retry that was implemented by somewhere else and tested.

Testing classes that make use of Spring 5 WebClient

We're slowly migrating some projects from using the legacy RestTemplate class to the new Spring 5 WebClient. As part of this, we have some existing test classes that make use of Mockito to verify that a given method will reach out to make a GET/POST/whatever to endpoint X using the template.
Given the fluent interface of the WebClient, the same mocking approach isn't really practical. I have spent some time using WireMock, which is great, but unfortunately there seems to be a bug where occasionally the WireMock tests will overrun or hang, and as such I'm considering alternatives.
Does anyone have any other suggestions for frameworks or techniques to use to verify that Spring's WebClient is making expected calls as part of SUT execution?
Spring actually uses OkHttp MockWebServer for testing of the WebClient.
Spring's Example Integration Tests
You can set up ordered mock responses or map mock responses to request details.
Approach 1 (preferred)
MockWebServer sounds like a cool approach(i.e Use WebClient for real, but mock the service it calls by using MockWebServer (okhttp)). An adapted example is:
#ExtendWith(MockitoExtension.class)
class serviceImplTest {
private ServiceImpl serviceImpl;
public static MockWebServer mockWebServer;
#BeforeAll
static void setUp() throws IOException {
mockWebServer = new MockWebServer();
mockWebServer.start();
}
#AfterAll
static void tearDown() throws IOException {
mockWebServer.shutdown();
}
#BeforeEach
void init() {
String baseUrl = String.format("http://localhost:%s", mockWebServer.getPort());
serviceImpl = new ServiceImpl(WebClient.builder(), baseUrl);
}
#Test
#DisplayName("whatever")
void methodName() {
mockWebServer.enqueue(new MockResponse().addHeader("header", "abcde123")); //MockWebServer will respond with the queued stub.
String header = serviceImpl.fetchHeader();
assertThat(header).isEqualTo("abcde123");
}
}
Approach 2
I have also tried the other approach, i.e Use Mockito to mimic the behavior of WebClient, and it's working fine. The only downside is that there is a lot of given()-willReturn(), to cater for each call in the chain(of the fluent WebClient API).

Spring: mocking security enhanced service

We'are imlementing part of our security at service layer, so I add #PreAuthorize annotation to some methods of MyService.
At MyServiceSecurityTest I want to test only security role-permission matrix, without any business logic. For that reason I have to mock MyService. the problem is that both Mockito and Spring security use CGLIB proxies, and my service is not enhanced with #PreAuthorize after Mockito.mock(MyService.class).
Is there any way to mock service and preserve #PreAuthorize logic?
Example:
#Service
public class MyService implements IMyService {
#Override
#PreAuthorize("hasAuthority('SYSOP')")
public void someMethod(ComplexDTO dto) {
// lots of logic and dependencies, require lots of stubbing.
}
}
In order to avoid initialization of all dependencies of MyService#someMethod and building ComplexDTO at MyServiceSecurityTest I want to mock MyServiceSecurityTest but preserve #PreAuthorize checks.
You need to do integration tests and not unit tests. In general, you do not see mock classes in integration tests, at least you would not mock the class you are testing, in this I case I guess its the MyService class.
Setting up integration tests involves reading up on, but the short example below should get you on the right path
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("myProfile")
public class MyServiceIT {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private TestRestTemplate restTemplate;
#Test
public void testMyService() {
logger.info("testMyService");
//user TestRestTemplate to call your service.
}
}
EDIT: In this integration test, Spring boots up normally. That means all the annotations for security are processed and all the beans it needs to create are created and properly injected. One thing you may have to control is the Spring profile.... that can be done with the #ActiveProfiles("myProfile") annotation, which I just added to the example.

spring aspect being applied outside of app context (in stubbed out unit test)

Here's a weird one. I've got a few tests failing because an aspect is being applied, so an autowired service is null, bad things ensue. The issue is that I can't understand how the aspect is even being applied, since in the test I construct the object under test with new.
#RunWith(MockitoJUnitRunner.class)
public class TheControllerTest {
#Spy
private TheController controller = new TheController();
#Mock
private HttpServletRequest request;
#Mock
private ConfigService configService;
....
#Before
public void setup() {
controller.setConfigService(configService);
....
}
#Test
public void testGetAccountsList() throws Exception {
Mockito.when(accountService.getAllAccounts()).thenReturn(Arrays.asList(account1, account2));
Map<String, Object> result = controller.getAccountsList(request);
...
}
}
I'm obviously omitting plenty of code, but really, I just don't understand how, given how controller is instantiated, it could have had the advice applied.
One possible reason could be if you are running this in Eclipse - in a project with ajbuilder enabled, even if you are explicitly expecting Spring AOP through dynamic proxies, ajbuilder would actually perform compile team weaving, and hence you would see advice enhanced classes even using normal "new". Can you please check this, the fix would be to disable "ajbuilder" - here is one reference - JUnit weaving wrong Spring AOP Bean

Resources