如何排除junit.framework.TestCase被Bytebuddy的@Origin拦截

How to exclude junit.framework.TestCase from being intercepted by Bytebuddy's @Origin

我正在尝试 运行 在执行期间进行 JUnit4 测试,然后使用 Bytebuddy(版本 1.2.3)拦截测试以找出测试中的哪些方法是增变器方法(更改底层 class).整个过程在正常的 JUnit 测试中运行良好,但是当我尝试执行完全相同的测试但扩展 junit.framework.TestCase 时,过程崩溃了。我设法将错误限制为以下内容:当我尝试调试时,我得到许多条目说 clazz="junit.framework.testcase"(请参阅 Interceptor.java),这是导致崩溃的 class。

我的问题如下。如何排除 TestCase class 被拦截?我尝试了以下语句:

  1. ElementMatchers.not(ElementMatchers.namedIgnoreCase("junit.framework.testcase"))
  2. ElementMatchers.not(ElementMatchers.is(junit.framework.TestCase.class))
  3. ElementMatchers.not(ElementMatchers.nameContains("junit.framework"))

使用以下代码,我尝试接收控制台输出:

intercepted: tests.producttest
running testSquarPrice
intercepted: example.product
finished testSquarPrice

但结果始终是以下输出,即使我尝试排除 junit.framework.testcase:

intercepted: junit.framework.testcase
intercepted: junit.framework.testcase
intercepted: junit.framework.testcase
intercepted: junit.framework.testcase
intercepted: junit.framework.testcase
intercepted: junit.framework.testcase
intercepted: junit.framework.testcase
intercepted: junit.framework.testcase
intercepted: junit.framework.testcase
intercepted: tests.producttest
running testSquarPrice
intercepted: example.product
finished testSquarPrice
intercepted: junit.framework.testcase

这是我的代码:
Starter.java

import introspect.Agent;

public class Starter {
    public static void main(String[] args) {
        Agent x = new Agent();
        x.run();
    }
}

Agent.java

package introspect;

import org.junit.runner.Request;

import net.bytebuddy.dynamic.DynamicType.Builder;

public class Agent {

    public Agent() {
    final Interceptor interceptor = new Interceptor();
    new AgentBuilder.Default()
            .type(ElementMatchers.not(ElementMatchers.named("junit.framework.testcase"))
                    .and(ElementMatchers.nameStartsWithIgnoreCase("example")
                            .or(ElementMatchers.nameEndsWithIgnoreCase("test"))),
                    ElementMatchers.not(ElementMatchers.isBootstrapClassLoader()))
            .transform(new AgentBuilder.Transformer() {
                @Override
                public Builder<?> transform(Builder<?> builder, TypeDescription arg1, ClassLoader arg2) {
                    return builder
                            .method(ElementMatchers.isPublic()
                                    .and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))))
                            .intercept(MethodDelegation.to(interceptor).filter(ElementMatchers.named("intercept")));
                }
            }).installOn(ByteBuddyAgent.install());
}

    public void run() {
        JUnitCore junit = new JUnitCore();

        Request request = Request.method(ProductTest.class, "testSquarPrice");
        junit.run(request);
    }
}

Interceptor.java

package introspect;

import java.lang.reflect.Method;

import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;

public class Interceptor {

    @RuntimeType
    public Object intercept(@Origin Method m, @SuperCall Callable<?> zuper, @AllArguments Object[] args,
            @This Object thiz) throws Exception {

        // get the name of the intercepted class
        String clazz = m.getDeclaringClass().getName().toLowerCase();
        System.out.println("intercepted: "+clazz);

        return zuper.call();
    }
}

ProductTest.java

package tests;

public class ProductTest extends TestCase{

    @Test
    public void testSquarPrice() {
        System.out.println("running testSquarPrice");

        Product p = new Product();

        int re = p.squarPrice(10);

        assertTrue(re == 100);

        System.out.println("finished testSquarPrice");
    }
}

Product.java

package example;

public class Product {

    public int count, price, index;

    public Product() {
        index = 0;
    }

    public int squarPrice(int price) {
        index++;
        return price * price;
    }
}

通过 ElementMatchers.not(ElementMatchers.is(junit.framework.TestCase.class)),您只排除了 class 本身。您不排除由此 class 声明并被其他 class 继承的方法。你想做的可能更像是:

method(ElementMatchers.isPublic()
  .and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))))
  .and(ElementMatchers.not(ElementMatchers.isDeclaredBy(TestCase.class))))

甚至喜欢:

method(ElementMatchers.isPublic()
  .and(ElementMatchers.isDeclaredBy(arg1)))

在后一种情况下,您只拦截声明的方法,而不会覆盖继承的方法。