0

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.

gtludwig
  • 5,411
  • 10
  • 64
  • 90
  • 1
    The answer is obvious: you have to satisfy the controller dependency. Inject in an instance of EndpointService. Your test has to do it in setup. Can be either a real or mock implementation - probably mock. – duffymo Oct 16 '19 at 14:38
  • 1
    This is a limitation of proxy based AOP, it will only work for public methods. Spring Boot by default forces cglib proxies, which work by subclassing the method and override/extend the public method to which AOP applies. It will wrap a configured object, and first call the interceptor then pass on to the actual object. However in your case you have a private method. A private method cannot be overriden and thus it will be called on the proxy, instead of eventually the real object. The proxy has no dependencis injected and thus it is null. Make the method public to fix. – M. Deinum Oct 16 '19 at 14:40
  • This question is primarily opinion-based and thus off-topic on SO. – kriegaex Oct 17 '19 at 07:39

2 Answers2

1

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.

Peter Walser
  • 15,208
  • 4
  • 51
  • 78
1

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.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • For now, we aim using it only for logging. But as I mentioned, it breaks the tests in place. – gtludwig Oct 16 '19 at 13:49
  • 1
    What does breaking look like? NPE? Can you make the aspect conditional so it's not applied for test profile or a property? https://stackoverflow.com/questions/52182570/spring-boot-load-bean-only-if-it-is-enabled-by-a-property – duffymo Oct 16 '19 at 13:50
  • For some of the failing tests, the test is supposed to hit an endpoint in a `@InjectMocks` annotated controller, but the `@Mock` annotated service is null. – gtludwig Oct 16 '19 at 14:00
  • 1
    That's not a problem with aspects, it's your mock. – duffymo Oct 16 '19 at 14:01
  • 1
    Doesn't mean you should dismiss the idea of AOP. It's perfectly valid. Maybe you have to write better tests. – duffymo Oct 16 '19 at 14:11
  • I agree with the idea of AOP. The part about writing better tests is the tricky one, though... this is a 20+ micro-services project and I would really do not want to re-write other peoples tests... Thank you for your inputs, thought! – gtludwig Oct 16 '19 at 14:14
  • "I am still missing on its returned value" - this sounds like a dismissal to me. NPE is easy to fix. Post your JUnit test code. Perhaps that will give a clue. – duffymo Oct 16 '19 at 14:16
  • By that, I meant that I had not yet seen the value, but I got your point. I have edited the question to add something very close to the implementation I am working. – gtludwig Oct 16 '19 at 14:31