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

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

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.

Is it worthy using AOP in a Spring-boot application?

A few days ago, I posted this question asking whether there is a newer approach to Spring AOP and mockito.
While I understand how to use AOP, I am still missing on its returned value. The whole endeavour has shown me that it's not really popular - at least there aren't that many recent posts.
If I comment out the annotations #Aspect and #Configuration in my LoggingAspect class, effectively rendering it non-aop, all my tests are green. If I switch it back on, I start getting a load of NullPointerExceptions and loads of other errors on my mocked test classes.
I wonder if it is worth the hassle.
EDIT adding more detail from my specific implementation.
Controller:
#RestController
public class EndpointController {
private EndpointService endpointService;
#Autowired
public EndpointController(EndpointService endpointService) {
this.endpointService = endpointService;
}
#PostMapping(path = "/endpoint", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
private #ResponseBody EndpointResponse doSomething(//... //, #RequestBody SomeObject someObject) throws Exception {
return endpointService.doSomething(someObject);
}
}
In my test class, I have:
#RunWith(SpringRunner.class)
public class EndpointControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldBeSuccessfulAccessingTheEndpoint() throws Exception {
SomeObject someObject = new SomeObject(// values //);
ObjectMapper mapper = new ObjectMapper();
String payload = mapper.writeValueAsString(someObject);
mockMvc.perform(post("/endpoint").contentType(MediaType.APPLICTION_JSON).content(payload)).andExpect(status().isOK));
}
}
It fails and throws a NullPointerException. When debugging, the endpointService is always null.
AOP is great for cross-cutting concerns:
Logging (we use it for access and performance logging)
Validation (such as used by Bean Validation, JSR-380)
Transactional scopes (built into frameworks such as JEE and Spring)
Security checks (e.g. Shiro)
and many more.
It could be used for other purposes, such as extending/wrapping existing functionality, though that is definitely not something I'd recommend, and fortunately never became popular, as it seems.
AOP is as valid as ever. It's used for transactions, logging, metrics, etc.
I think there was period where it might have been overused as decorators.
Production and testing are different matters.
If you're unit testing a class, it suggests that you aren't testing the aspects. You could make those aspects conditional based on profile.
If the proper operation of your object depends on the aspect, because it modifies the input, perhaps you should rethink.

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.

How to disable Autowiring in Spring JUnit Tests?

Im currently working on a tool, which analyzes ApplicationContexts of Spring applications. To start the analysis I had implemented an abstract JUnit-Test class, which has to be extended by an concrete instance in the target application. This makes it very easy to write own Tests for further context-files and my tool could use the context loading functionality as well.
The problem for me is that Spring-based JUnit-Tests automatically enable Autowiring, even when it is not enabled in the target context. But I need the unchanged context, to ensure the correctness of the analysis results.
My abstract Test-Class which is embedded in the analysis tool looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
public abstract class AbstractContextLoader implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Test
public void analyseContext() {
SpringContextAnalysis.startApplication(applicationContext);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
A concrete implementation could look like this:
#ContextConfiguration(locations="context.xml")
public class ContextAnalysis extends AbstractContextLoader {
}
Is it possible to disable the automatic instantiation of Autowiring-Components while using Spring-based JUnit-Tests?
Maybe you should try storing it in an Object and casting it when needed? You'll need to do the same with the setter method's parameter.
Otherwise you could try annotating it with #Autowired(required=false), but I doubt that would work.

EasyMock object for unit testing involving scope="request" bean

I am trying to add some Unit Testing to some of our companies code. Yes, I know it should already be there, but not everyone seems to have the same view of unit testing that I do.
However, I have come against a bit of a stopper for me. Admittedly, my Java, Spring and Unit Testing knowledge are not all that they should be. My problem is this though:
I have added a unit test to my code, which tests a class. This class includes a bean which has scope="request", and when it tries to instantiate the bean it throws an exception:
java.lang.IllegalStateException: No Scope registered for scope 'request'
I believe this is because I don't have a HttpServletRequest object, but I don't know how to create a mock one of these and also I don't know how, once created, to add this Mock Object to the unit test so that it resolves this problem.
Below is a cut down version of the code involved, which I believe includes all of the details that are part of this problem.
How can I get this to work?
#Test
public void handleRequest() {
try {
Message<?> outMessage = (Message<?>) response.handleRequest(map);
} catch (Exception e) {
assertNotNull(e);
}
outMessage.getPayload().toString());
}
public class upddResponse extends AbstractResponseTransform {
#SuppressWarnings("unchecked")
public Message<?> handleRequest(Map<String, Message<?>> messages) throws Exception {
super.addEnvironmentDetails(serviceResponseDocument.getServiceResponse());
}
public abstract class AbstractResponseTransform implements ResponseTransform,
ApplicationContextAware {
private ApplicationContext applicationContext;
private MCSResponseAggregator mcsResponseAggregator;
public ServiceResponseType addEnvironmentDetails(ServiceResponseType serviceResponse) throws Exception {
try {
mcsResponseAggregator = (MCSResponseAggregator) applicationContext
.getBean("mcsResponseAggregator");
}
catch (Exception ex) {
}
}
}
public interface ResponseTransform extends Transform {
public Message<?> handleRequest(Map<String, Message<?>> messages)
throws Exception;
}
<bean id="mcsResponseAggregator" class="com.company.aggregator.MCSResponseAggregator" scope="request" />
You need a WebApplicationContext to handle beans with: scope="request"
I recommend to use stub objects with Spring integration tests and use EasyMock without Spring when you test a class isolated.
You can use mocks within the Spring Context:
but that will not solve your problem as it will not make Spring understand scope="request". You can create your own implementation of the request scope, but I'm getting the feeling that you're better off not going through all this trouble.
The easy way out would be to override your request scoped bean in a little test context. You're technically not testing the original context then, but you will be done a lot quicker.
Spring 3.2 comes with support for this. See "Spring MVC Test Framework"

Resources