I am new to AspectJ and reflections What I want to achieve is something like In the below example:
A test class :
public class Sample {
Home home = new Home();
Account account = new Account();
AccountAuthentication accountAuthentication = new AccountAuthentication();
#Test
public void loginInvalidCredentials(){
home.clickOnAccount();
account.login("Admin", "secret");
accountAuthentication.waitForPage();
Assert.assertTrue(true);
}
}
I want to log Output something like this :
packageName.Sample.loginInvalidCredentials
packageName.Home.clickOnAccount();
packageName.Account.login(userName = "Admin", Password ="secret");
packageName.AccountAuthentication.waitForPage();
packageName.Assert.assertTrue(value= true);
I have accessed the name of function packageName.Sample.loginInvalidCredentials with AspectJ
#Aspect
public class AspectClass {
#Around("execution(* *(..)) && #annotation(org.testng.annotations.Test)")
public void around(ProceedingJoinPoint point) throws Throwable {
Method method = MethodSignature.class.cast(point.getSignature()).getMethod();
String methodName = method.getName();
System.out.println("Aspect called for method "+ point.getSignature().getDeclaringType().name +"."+methodName);
try {
//TODO intercept each function call inside the method without any custom anotation and get the value of parameters as well
joinPoint.proceed();
}
}
}
Thanks in advance.
I am assuming that
you use full AspectJ and not something like Spring AOP (because then the answer would not apply),
you do not want to record method calls recursively but just the ones directly called from your #Test methods. For example, if Account.login(..) would internally call Account.checkPassword(..), it should not be recorded. That would also be possible, but then the solution would look differently.
all you want to do is log test execution and not do anything else, so an #Around advice is not necessary, #Before is enough.
Dummy application classes:
package de.scrum_master.app;
public class Account {
public void login(String user, String password) {
checkPassword(password);
}
public void checkPassword(String password) {}
}
package de.scrum_master.app;
public class AccountAuthentication {
public void waitForPage() {}
}
package de.scrum_master.app;
public class Home {
public void clickOnAccount() {
doSomethingElse();
}
public void doSomethingElse() {}
}
Marker annotation:
I created this one myself because I was too lazy to set up a project with TestNG which I normally do not use.
package org.testng.annotations;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(METHOD)
public #interface Test {}
Sample application:
package de.scrum_master.app;
import org.testng.annotations.Test;
public class Sample {
Home home = new Home();
Account account = new Account();
AccountAuthentication accountAuthentication = new AccountAuthentication();
#Test
public void loginInvalidCredentials() {
home.clickOnAccount();
account.login("Admin", "secret");
accountAuthentication.waitForPage();
}
public static void main(String[] args) {
new Sample().loginInvalidCredentials();
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class TestExecutionLogger {
#Before("execution(* *(..)) && #annotation(org.testng.annotations.Test)")
public void logTest(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
#Before("call(* *(..)) && withincode(#org.testng.annotations.Test * *(..))")
public void logTestActions(JoinPoint joinPoint) {
System.out.println(" " + joinPoint);
}
}
Console log:
execution(void de.scrum_master.app.Sample.loginInvalidCredentials())
call(void de.scrum_master.app.Home.clickOnAccount())
call(void de.scrum_master.app.Account.login(String, String))
call(void de.scrum_master.app.AccountAuthentication.waitForPage())
Of course you can refine your aspect if you really think you need to strip the plain method names from the informative AspectJ log output:
#Before("execution(* *(..)) && #annotation(org.testng.annotations.Test)")
public void logTest(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature());
}
#Before("call(* *(..)) && withincode(#org.testng.annotations.Test * *(..))")
public void logTestActions(JoinPoint joinPoint) {
System.out.println(" " + joinPoint.getSignature());
}
void de.scrum_master.app.Sample.loginInvalidCredentials()
void de.scrum_master.app.Home.clickOnAccount()
void de.scrum_master.app.Account.login(String, String)
void de.scrum_master.app.AccountAuthentication.waitForPage()
You can further refine it if you deliberately want to remove the method's return type or include the actual parameter values like in your example, which is easily possible via JoinPoint.getArgs(), but I leave that up to you. Your question was about pointcuts, not about how to extract which information from joinpoints.
Related
I'm developing an Advice and I want to make it advise only on field sets in specific methods.
I tried cflow(pointcutForSpecificMethod()) && set(* *) pointcut expression but it picks field sets in other methods under control flow of specific methods.
Any idea?
This is not possible directly with an exact pointcut expression, but you can use if() pointcuts to dynamically determine from the stack trace or - like in this case - from the enclosing join point static part exposed by AspectJ - what the executing method is. Here is a little example and an aspect in two variants: native syntax (my preference, more elegant and less boilerplate) and annotation-style syntax:
package de.scrum_master.app;
public class Application {
private int field = 0;
public static void main(String[] args) {
Application app = new Application();
app.foo(11);
app.bar(22);
}
public void foo(int i) {
field = i;
bar(2 * i);
}
void bar(int i) {
field = i;
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
public aspect MyAspectNative {
pointcut pointcutForSpecificMethod() : execution(* foo(*));
public static boolean executingMethodMatches(JoinPoint.StaticPart staticPart) {
return staticPart.getSignature().toLongString().contains("de.scrum_master.app.Application.foo(int)");
}
before() :
cflow(pointcutForSpecificMethod()) && set(* *) &&
if(executingMethodMatches(thisEnclosingJoinPointStaticPart))
{
System.out.println(thisEnclosingJoinPointStaticPart);
System.out.println(thisJoinPoint);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class MyAspect {
#Pointcut("execution(* foo(*))")
private static void pointcutForSpecificMethod() {}
#Pointcut("if()")
public static boolean executingMethodMatches(JoinPoint.EnclosingStaticPart staticPart) {
return staticPart.getSignature().toLongString().contains("de.scrum_master.app.Application.foo(int)");
}
#Before(
"cflow(pointcutForSpecificMethod()) && set(* *) && " +
"executingMethodMatches(thisEnclosingJoinPointStaticPart)"
)
public void beforeAdvice(JoinPoint thisJoinPoint, JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart)
{
System.out.println(thisEnclosingJoinPointStaticPart);
System.out.println(thisJoinPoint);
}
}
I tried to keep the two aspects as similar as possible structurally. No matter which aspect syntax variant you choose, the output will be:
execution(void de.scrum_master.app.Application.foo(int))
set(int de.scrum_master.app.Application.field)
Need to run before and after methods on some annotations.
Not using spring, no xml. Is it possible to have some kind of AOP engine that I set from main() so that it can be invoked whenever needed? It's also OK for me to put in a method to manually call an evaluation method.
Example:
public void doThis(#RequiredSecurityRole("admin") user){
doAOPStuff();
}
before() get from the db and check if user is admin, throws Exception if it's not admin.
after() log into db the action.
How to achieve this?
You can do this yourself using the java.lang.reflex.Proxy class. It does require that the code you're proxying be defined in an interface.
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DoItYourAop {
public static void main(String[] args) {
SaysHello saysHello = new SaysHelloImpl();
InvocationHandler logger = new LoggingProxy(saysHello);
SaysHello proxy = (SaysHello) Proxy.newProxyInstance(SaysHello.class.getClassLoader(),
new Class[]{SaysHello.class}, logger);
proxy.sayHello();
}
public interface SaysHello {
void sayHello();
void sayGoodbye();
}
public static class SaysHelloImpl implements SaysHello {
#Log
#Override
public void sayHello() {
System.out.println("Says Hello");
}
#Override
public void sayGoodbye() {
System.out.println("Says Goodbye");
}
}
#Retention(RUNTIME)
#interface Log {
}
public static class LoggingProxy implements InvocationHandler {
private final Object proxied;
public LoggingProxy(Object proxied) {
this.proxied = proxied;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Method proxiedMethod = proxied.getClass().getMethod(method.getName(), method.getParameterTypes());
boolean log = proxiedMethod.isAnnotationPresent(Log.class);
if (log) {
System.out.println("Before");
}
Object result = method.invoke(proxied, args);
if (log) {
System.out.println("After");
}
return result;
}
}
}
I need help to write some Aspectj advice on this particular case:
Suppose we have this class:
package org.group;
public class Person {
public void method1(String id, String number) {
//some code
List<String> list = getList(number);
//some code
}
public List<String> getList(String number) {
return Arrays.asList(number);
}
}
I want to create an Aspectj advice into method1 to get the result of getList. I try this:
#Pointcut("execution(* org.group.Person.getList(..))")
public void methodGetList() {
}
#AfterReturning(pointcut = "methodGetList()", returning = "result")
public void afterMethodGetList(JoinPoint joinPoint, List<String> result) {
System.out.println("I can see the list result: " + result.toString());
}
This advice works on all executions of getList method, but what I want exactly, is to get the result inside the method1 call to get an information with the method1's id , for example like this:
'I can see the list result [4] for the person with id : XXX'
Thank you for your help.
You need to limit your pointcut to the executions within the control flow - cflow() - of the calling method and also bind the calling method's parameter of interest via args().
Application:
package org.group;
import java.util.Arrays;
import java.util.List;
public class Person {
public void method1(String id, String number) {
// some code
List<String> list = getList(number);
// some code
}
public List<String> getList(String number) {
return Arrays.asList(number);
}
public static void main(String[] args) {
// Should not be intercepted
new Person().getList("22");
// Should be intercepted
new Person().method1("John Doe", "11");
}
}
Aspect:
package de.scrum_master.aspect;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class MyAspect {
#Pointcut("execution(* org.group.Person.getList(..))")
public void methodGetList() {}
#Pointcut("execution(* org.group.Person.method1(..)) && args(id, *)")
public void methodMethod1(String id) {}
#AfterReturning(
pointcut = "methodGetList() && cflow(methodMethod1(id))",
returning = "result"
)
public void afterMethodGetList(JoinPoint joinPoint, String id, List<String> result) {
System.out.println(
"I can see the list result " + result +
" for the person with id " + id
);
}
}
Console log:
I can see the list result [11] for the person with id John Doe
I am trying to combining multiple pointcuts of getter and setter to create an advice that will be executed if both pointcuts are executed. I have tried in normal AspectJ class and annotation #Aspect class but still it gives me warning adviceDidNotMatch and eventually the advice is not executed. Strangely if I change && (AND) with || (OR) it works, but why && doesn't work at all?
Here is the advice declared in normal AspectJ class.
package testMaven;
pointcut getter() : execution(* testMaven.testing.getDd(..));
before() : getter(){
System.out.println("test get");
}
pointcut setter() : execution(* testMaven.testing.setDd(..));
before() : setter(){
System.out.println("test set");
}
pointcut combine(): getter() && setter();
before(): combine(){
System.out.println("testing combine");
}
}
Here is the advice declared in annotation #Aspect class
package testMaven;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
#Aspect
public class aspecter {
#Pointcut("call (* testMaven.testing.getDd(..))")
public void getter(){
}
#Pointcut("call (* testMaven.testing.setDd(..))")
public void setter(){}
#Pointcut("execution (* testMaven.tester.setZ(..))")
public void setterZ(){}
#Before("setterZ()")
public void settingZ(){
System.out.println("before set Z");
}
#Pointcut("getter() && setter()")
public void getterSetter(){}
#After("getterSetter()")
public void testerd(){
System.out.println("works");
}
#Pointcut("getter() && setterZ()")
public void getterSetter2(){}
#After("getterSetter2()")
public void testinger(){
System.out.println("ok");
}
}
Here is the testing class that I want to be advised:
package testMaven;
public class testing {
public int dd;
public int getDd() {
return dd;
}
public void setDd(int dd) {
this.dd = dd;
}
}
package testMaven;
public class testing {
public int dd;
public int getDd() {
return dd;
}
public void setDd(int dd) {
this.dd = dd;
}
public void aa(int a){
System.out.println(a);
}
}
And here is the main class:
package testMaven;
public class MainApp {
public static void main(String[] args) {
// TODO Auto-generated method stub
testing test = new testing();
test.aa(2);
test.setDd(3);
tester et = new tester();
et.setZ(3);
et.printNo(1000);
System.out.println(test.getDd());
}
}
Is there something wrong with my code? Any help is appreciated.
Thanks
You asked if there is something wrong with your code. The answer is yes. The two pointcuts setter() and getter() are mutually exclusive. Thus combining them with && - i.e. creating an intersection of two mutually disjunct sets of joinpoints - logically leads to an empty result set. Ergo your combined pointcut does not match. You should use || as uniknow suggested in his/her comment.
If you want to achieve something else, please explain it in a comprehensible way, if necessary by giving examples, in a comment or by updating your question. I really did not get what you really want.
Is there a way in JUnit to detect within an #After annotated method if there was a test failure or error in the test case?
One ugly solution would be something like that:
boolean withoutFailure = false;
#Test
void test() {
...
asserts...
withoutFailure = true;
}
#After
public void tearDown() {
if(!withoutFailuere) {
this.dontReuseTestenvironmentForNextTest();
}
}
This is ugly because one need to take care of the "infrastructure" (withoutFailure flag) in the test code.
I hope that there is something where I can get the test status in the #After method!?
If you are lucky enough to be using JUnit 4.9 or later, TestWatcher will do exactly what you want.
Share and Enjoy!
I extend dsaff's answer to solve the problem that a TestRule can not execute some code snipped between the execution of the test-method and the after-method. So with a simple MethodRule one can not use this rule to provide a success flag that is use in the #After annotated methods.
My idea is a hack! Anyway, it is to use a TestRule (extends TestWatcher). A TestRule will get knowledge about failed or success of a test. My TestRule will then scan the class for all Methods annotated with my new AfterHack annotations and invoke that methods with a success flag.
AfterHack annotation
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(METHOD)
public #interface AfterHack {}
AfterHackRule
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
public class AfterHackRule extends TestWatcher {
private Object testClassInstance;
public AfterHackRule(final Object testClassInstance) {
this.testClassInstance = testClassInstance;
}
protected void succeeded(Description description) {
invokeAfterHackMethods(true);
}
protected void failed(Throwable e, Description description) {
invokeAfterHackMethods(false);
}
public void invokeAfterHackMethods(boolean successFlag) {
for (Method afterHackMethod :
this.getAfterHackMethods(this.testClassInstance.getClass())) {
try {
afterHackMethod.invoke(this.testClassInstance, successFlag);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException("error while invoking afterHackMethod "
+ afterHackMethod);
}
}
}
private List<Method> getAfterHackMethods(Class<?> testClass) {
List<Method> results = new ArrayList<>();
for (Method method : testClass.getMethods()) {
if (method.isAnnotationPresent(AfterHack.class)) {
results.add(method);
}
}
return results;
}
}
Usage:
public class DemoTest {
#Rule
public AfterHackRule afterHackRule = new AfterHackRule(this);
#AfterHack
public void after(boolean success) {
System.out.println("afterHack:" + success);
}
#Test
public void demofails() {
Assert.fail();
}
#Test
public void demoSucceeds() {}
}
BTW:
1) Hopefully there is a better solution in Junit5
2) The better way is to use the TestWatcher Rule instead of the #Before and #After Method at all (that is the way I read dsaff's answer)
#see
I don't know any easy or elegant way to detect the failure of a Junit test in an #After method.
If it is possible to use a TestRule instead of an #After method, one possibility to do it is using two chained TestRules, using a TestWatcher as the inner rule.
Example:
package org.example;
import static org.junit.Assert.fail;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
public class ExampleTest {
private String name = "";
private boolean failed;
#Rule
public TestRule afterWithFailedInformation = RuleChain
.outerRule(new ExternalResource(){
#Override
protected void after() {
System.out.println("Test "+name+" "+(failed?"failed":"finished")+".");
}
})
.around(new TestWatcher(){
#Override
protected void finished(Description description) {
name = description.getDisplayName();
}
#Override
protected void failed(Throwable e, Description description) {
failed = true;
}
})
;
#Test
public void testSomething(){
fail();
}
#Test
public void testSomethingElse(){
}
}