Description
I have a unit test for which I do not want the AOP to be loaded. I don't load the AOP in any of the unit test code. The classes which are not mocked do not have any AOP/beans/components autowired and or used.
On running the unit test the code is suppose to throw a custom exception called FrameworkException.
However the AOP catches the exception and runs the AfterThrowing Advice. I don't want this in the unit test.
Can anyone help?
The Problem
Why is the ExceptionAspect afterThrowing() advice still being called? It was not called previously.
Suspicions
Does mock code still instantiate with any existing functionality?
Attempted resolutions
- I have tried loading an AnnotationConfigApplicationContext which loads an empty configuration class. This did not seem to work.
Code Example - Unit Test
#Test
public void processRequest_WithRequestParameterNull_ExceptionExpected()
{
try
{
RequestWrapper requestMock = Mockito.mock(RequestWrapper.class);
Auditor auditorMock = Mockito.mock(Auditor.class);
CoreWrapper coreMock = Mockito.mock(CoreWrapper.class);
RequestAssessmentStatusHandler handler = new RequestAssessmentStatusHandler(requestMock, auditorMock,
coreMock);
handler.processRequest(null);
fail("processRequest_WithRequestParameterNull_ExceptionExpected failed.");
}
catch (FrameworkException e)
{
assertEquals(EventIds.INVALID_FRAMEWORK_PARAMETER, e.getEventId());
}
catch (Exception e)
{
fail("processRequest_WithRequestParameterNull_ExceptionExpected unhandled exception: "
+ e.getStackTrace().toString());
}
}
Code Example - AfterThrowing Advice
#Component
#Aspect
public class ExceptionAspect
{
#AfterThrowing(pointcut = "execution(* *(..))", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) throws Exception
{
// Do things here.
}
}
You could add #Profile("!test") to your aspect component and then add #ActiveProfiles("test") annotation to your test.
Related
I am writing a Spring Boot Application. I want to audit methods with my annotation #AuditMetod: For example I have method foo() with the annotation:
#AuditMetod(name = "SomeValue")
foo() {...}
I want to handle and audit such methods like this (the simplest example):
auditMethod(Method method) {
if (method.hasAnnotation(AuditMethod.class)) {
System.out.println (method.getName() + " was called at " + new Date())
}
}
upd
Thanks to #Karthikeyan #Swapnil Khante and #misha2048 I understood, that I need to use AOP. But I have 2 problems:
The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
How can I check in aspect method what method it is intercepting. To get an instance of Method class.
Now I have the following code:
Controller:
#PostMapping
#LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
public ResponseEntity<?> create(#Valid #RequestBody SubscriptionRequestDto dto) {
...
}
Aspect:
`#Aspect
#Slf4j
#Component
public class AuditAspect {
#Pointcut(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------ASPECT METHOD IS CALLED------------");
}`
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LoggingRest {
String executor() default "SYSTEM";
String method() default "";
String model() default "";
}
Auditing is a cross-cutting concern and can be handled using AOP.
Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptorto write your business logic.
To use the Spring interceptor you will need to implement the HandlerInterceptor interface
Example of the annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Audit {
boolean active() default true;
}
Interceptor example
#Component
public class AuditInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);
if (annotation != null && annotation.active()) {
// your business logic
}
}
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
check this interceptor example
I think one of the solutions here, as #Karthikeyan mentioned, is to use Spring AOP.
If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with #Aspect). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.
For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):
#SpringBootApplication
#EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {
#Autowired
private BusinessLogicClass businessLogicClass;
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
businessLogicClass.test();
}
}
#Aspect
#Component
class MyAspectLogicClass {
#Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long before = System.currentTimeMillis();
Object returnedValue = proceedingJoinPoint.proceed();
long after = System.currentTimeMillis();
System.out.printf("Retruned in '%s' ms %n", (after - before));
return returnedValue;
}
}
#Component
class BusinessLogicClass {
public void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In my case, I will get the time before method execution, then by the means of
proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.
I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:
https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html offical spring doc (stale a bit, but there are some valuable things to learn)
https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html is more fresh doc
Hope it helped :)
The problem was in right annotation. In Aspect class I tried #Around and everything works as I need.
#Aspect
#Slf4j
#Component
public class AuditAspect {
#Around(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
log.info("----------ASPECT METHOD IS CALLED------------");
}
}
For getting a Method instance I use fallowing code
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
I am trying to get some context of the result of the test run in the #AfterTest. I would like to have, at bare minimum, knowledge if it passed or not and ideally also the thrown exception if there is one.
However, every parameter I try seems to not be resolvable and I can't find any documentation on what should be available.
Code:
public class TestClass {
#AfterEach
public void afterEach(
TestInfo testInfo, //works, but no report on state of test
// none of these work
TestExecutionSummary summary,
TestExecutionResult result,
TestFailure fail,
Optional<Throwable> execOp,
Throwable exception
) {
// ...
}
}
What can I do to get this context?
Not sure if this is what you want, but you can use either a TestExecutionListener or a TestWatcher (there are also other ways that you can check in documentation).
An example of TestWatcher can be found here: TestWatcher in junit5 and a more detailed explanation here: https://www.baeldung.com/junit-testwatcher.
The following code example was partially taken from here.
public class TestResultLoggerExtension implements TestWatcher, AfterAllCallback {
...
#Override
public void testSuccessful(ExtensionContext context) {
System.out.println("Test Successful for test {}: ", context.getDisplayName();
}
#Override
public void testFailed(ExtensionContext context, Throwable cause) {
System.out.println("Test Failed for test {}, with cause {}", context.getDisplayName(), cause.getMessage());
}
}
You test class would be something like this:
#ExtendWith(TestResultLoggerExtension.class)
public class TestClass {
You can adapt the logic to your needs.
More References:
https://junit.org/junit5/docs/current/user-guide/#extensions-test-result-processing
https://junit.org/junit5/docs/current/user-guide/#launcher-api-listeners-custom
I have a notification service(I have control of this class).
If there is any unchecked exception, then I do not want to throw it. But instead want to log it in a specific manner.
I can directly have try catch block in each method's implementation but I am in search of some magic here 😄
Is there a common way that this can be handled through Spring?
Update:
AOP is also a way to do it. For example: https://dzone.com/articles/handling-exceptions-using-springs-aop
Any other direct implementation for this?
This was my requirement but I was not able to find anything with respect to this.
I faced similar issues when dealing with calling multiple apis from rest service, where i was suppose to provide a fallback implementation when error occured. My Aspect was more than what i am giving example here.
Service
#Service
public class SuspiciousService {
final Random random = new Random();
public String beSuspicious() {
final boolean value = random.nextBoolean();
if (value) {
throw new IllegalStateException("Exception occured for: " + value);
}
return "I am not suspicious";
}
}
Sample service which randomly throws an error.
Controller
#RestController
#RequestMapping("/is-suspicious")
#AllArgsConstructor
public class SampleController {
private final SuspiciousService suspiciousService;
#GetMapping
public Map<String, String> get() {
return Map.of("isSuspicious", suspiciousService.beSuspicious());
}
}
Controller which invokes this service.
Finally, Around Aspect catches this exception and provides the sample response.
#Aspect
#Component
#Order(2)
public class AspectAroundSuspiciousService {
#Around("execution(* in.silentsudo.sprintbootresttemplate.SuspiciousService.beSuspicious(..))")
public Object parallelExecuteBeforeAndAfterCompose(ProceedingJoinPoint point) throws Throwable {
try {
return point.proceed();
} catch (RuntimeException re) {
return "Yes, I am suspicious";
}
}
}
The other approach is if you are using ByteBuddy, you can add annotation to the method throwing exception
#Advice.OnMethodExit(onThrowable = RuntimeException.class)
and have an ExceptionHandler to cath this
#ExceptionHandler
private String suspiciousRuntimeException(RuntimeException exception) {
return "Yes, I am suspicious from ex handler, error: " + exception.getMessage();
}
I choose aspect over bytebuddy for simple reason as i was handling ladder of api exception, where as this implementation will catch in general RuntimeException happenning from service#method
I have a Spring Boot application where I have methods in my Service layer like:
public List<PlacementDTO> getPlacementById(final int id) throws MctException {
List<PlacementDTO> placementList;
try {
placementList = placementDao.getPlacementById(id);
} catch (SQLException ex) {
throw new MctException("Error retrieving placement data", ex);
}
return placementList;
}
What is the best way to unit test that MctException will be thrown? I tried:
#Test(expected = MctException.class)
public void testGetPlacementByIdFail() throws MctException, SQLException {
when(placementDao.getPlacementById(15)).thenThrow(MctException.class);
placementService.getPlacementById(15);
}
However, this doesn't test the right that an exception is actually thrown.
I think you have to stub the placementDao.getPlacementById(15) call to throw the SQLException instead of your MctException, like this:
#Test(expected = MctException.class)
public void testGetPlacementByIdFail() throws MctException, SQLException {
when(placementDao.getPlacementById(15)).thenThrow(SQLException.class);
placementService.getPlacementById(15);
}
This way when you call your Service method placementService.getPlacementById(15); you know that your MctException will encapsulate the SQLException and therefore your test could expect the MctException exception to be thrown.
You may want to try out the ExepctionException rule feature of Junit. This would allow greater granularity in verification of your exception handling in your unit test than the expected exception annotation.
#Rule
public ExpectedException thrown= ExpectedException.none();
#Test
public void testGetPlacementByIdFail(){
thrown.expect(MctException.class);
thrown.expectMessage("Error retrieving placement data");
//Test code that throws the exception
}
As the above snippet show, you would also be able to test on various properties of the exception like its message.
I want to test a JSF Backing-Bean method "isInProgress" that delegates to a service method "isInProgress". When the service method throws an exception, the bean should put an event on a specific event logger and return false.
When I debug the following test, I get into the catch-block. The mocked service does not throw the exception, but returns a "default answer" which is false for th boolean. What am I doing wrong?
I also wonder if the try-catch around the "when" call can be avoided somehow, as the actual exception is swallowed by the bean under test. In fact I think "declaratively" passing the name of the method to the "when" should suffice. Is there a way to get that cleaner ?
#Test
public void testIsInProgressExeption() {
//prepare object and inputs
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
try {
when(bean.getService().isInProgress()).thenThrow(new Exception());
} catch (Exception e) {
//prepare expected object and result
MyBean expectedBean = new MyBean();
expectedBean.setService(service);
boolean expected = false;
//execute method under test
boolean actual = bean.isInProgress();
//check return values and exceptions
assertEquals(expected, actual);
//check that bean did not change unexpectedly
assertTrue(bean.equals(expectedBean));
//check sideeffects on event log
assertTrue(logEvents.containsMessage("MDI09"));
}
}
For reference here is the updated Test:
#Test
public void testIsInProgressExeption() throws Exception {
//prepare object and inputs
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
when(bean.getService().isInProgress()).thenThrow(new Exception());
//prepare expected object and result
MyBean expectedBean = new MyBean();
expectedBean.setService(service);
boolean expected = false;
//execute method under test
boolean actual = bean.isInProgress();
//check return values and exceptions
assertEquals(expected, actual);
//check that bean did not change unexpectedly
assertTrue(bean.equals(expectedBean));
//check sideeffects on event log
assertTrue(logEvents.containsMessage("MDI09"));
}
Move the when clause out of the try block and change it to:
when(service.isInProgress()).thenThrow(new Exception());
Now it should throw an exception when called.
For the records, I was doing state-base testing. Interestingly, Fowler posted in http://martinfowler.com/articles/mocksArentStubs.html a very nice article that goes quite the same route but then differentiates it from mocking and interaction-based testing.
You are doing it wrong. First you should lay out your test with the BDD or AAA keywords, with BDD :
#Test public void testIsInProgressExeption() {
// given
// when
// then
}
In the given part you will write your fixture, i.e. the setup of your test scenario. In the when part you will call the production code i.e. the tested subject. Lastly in the when part you will write your verifications and or assertions.
Stubs go in the fixture, so this line is misplaced, it doesn't belong here, it' just a definition of the behavior.
when(bean.getService().isInProgress()).thenThrow(new Exception());
However you should directly the service reference instead of the bean.getService(), this is akward.
I don't really understand why you are creating a new instance of the bean in the catch clause, this is weird. But here's how I wold write the test. Note by the way I explain in the unit test name what behavior the test is actully testing, writing this in camel case is way to painful to read, so I use the underscored convention, it's ok in tests.
#Test public void when_service_throw_Exception_InProgress_then_returns_false() throws Exception {
// given
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
when(service.isInProgress()).thenThrow(new Exception());
// when
boolean result = bean.isInProgress();
// then
assertFalse(result);
}
Also I would split the assertion on the event, this a different behavior :
#Test public void when_service_throw_Exception_InProgress_then_log_event_MDI09() throws Exception {
// given
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
// somehow set up the logEvents collaborator
when(service.isInProgress()).thenThrow(new Exception());
// when
bean.isInProgress();
// then
assertTrue(logEvents.containsMessage("MDI09"));
}
You can even go further to simplify the fixture, if you use JUnit, you write this code :
#RunWith(MockitoJUnitRunner.class)
public class MyBeanTest {
#Mock MyService service;
#Mock LogEvents logEvents;
#InjectMocks MyBean bean;
#Test public void when_service_throw_Exception_InProgress_then_log_event_MDI09() throws Exception {
// given
when(service.isInProgress()).thenThrow(Exception.class);
// when
bean.isInProgress();
// then
verify(logEvents).logEvent("MDI09");
}
}
In the example above I also extrapolated on the log event stuff, but it is just to give the idea of what's possible.