Перехватить все вызовы функций внутри перехваченной функции с помощью аспектаj - PullRequest
0 голосов
/ 08 мая 2020

Я новичок в AspectJ и отражениях. Я хочу добиться чего-то вроде. В приведенном ниже примере:

Тестовый класс:

    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);
    }
}

Я хочу вести журнал. :

packageName.Sample.loginInvalidCredentials 
    packageName.Home.clickOnAccount();
    packageName.Account.login(userName = "Admin", Password ="secret");
    packageName.AccountAuthentication.waitForPage();
    packageName.Assert.assertTrue(value= true);

Я получил доступ к имени функции packageName.Sample.loginInvalidCredentials с помощью 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();
             } 
    }
}

Заранее спасибо.

1 Ответ

0 голосов
/ 09 мая 2020

Я предполагаю, что

  • вы используете полный AspectJ, а не что-то вроде Spring AOP (потому что тогда ответ не будет применяться),
  • вы используете не хотите записывать вызовы методов рекурсивно, но только те, которые вызываются напрямую из ваших @Test методов. Например, если Account.login(..) вызовет внутренний вызов Account.checkPassword(..), это не должно быть записано. Это тоже возможно, но тогда решение будет выглядеть по-другому.
  • все, что вам нужно, - это регистрировать выполнение теста и больше ничего не делать, поэтому совет @Around не нужен, @Before достаточно .

Классы фиктивных приложений:

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() {}
}

Аннотации маркера:

Я создал его сам, потому что мне было лень создавать проект с TestNG, который я обычно не использую.

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 {}

Пример приложения:

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();
  }
}

Аспект:

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);
  }
}

Журнал консоли:

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())

Конечно, вы можете уточнить свой аспект, если действительно думаете вам необходимо удалить простые имена методов из информативного вывода журнала AspectJ:

  @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()

Вы можете дополнительно уточнить его, если вы намеренно хотите удалить тип возвращаемого метода или включить фактические значения параметров, как в ваш пример, который легко возможен через JoinPoint.getArgs(), но я оставляю это на ваше усмотрение. Ваш вопрос касался срезов точек, а не того, как извлекать информацию из точек соединения.

...