从调用堆栈中检索最顶层注释的函数

Function to retrieve the top-most annotation from the call stack

我想定义一个抽象基础 class,它定义了我想在测试各种 API 时一遍又一遍地实现的通用可重用测试用例。这个想法是定义一次测试,然后我可以继承通用测试以获得测试定义,而我所有的测试代码所要做的就是实现如何执行测试的细节。这样我就不会错过好的测试用例,我不必重新发明轮子,也不必在我想出另一个好的测试用例进行测试时在几十个地方进行更改。

本质上,我在进行参数化测试,其中参数在基 class 中定义。预期结果也在基class中定义。但是,有时需要覆盖预期结果。例如,一个普通的字符串字段可能允许任何字符,而一个对象名称字段可能(或者在我的例子中确实)限制哪些字符是合法的。例如,字符串字段允许“?”在值中,名称字段没有。

我试图找出一种干净的方法来定义参数 ONCE,在基础 class 中,并且只定义一次预期结果,但可以选择在实现中覆盖它 class 在上下文需要的地方。

我遇到的问题是,当你覆盖一个方法时,你必须替换它的实现。你不能只替换它的一部分。我想在不覆盖参数部分的情况下替换预期结果。我也不认为将每个测试分成两种方法是一种干净或理想的解决方案,一种提供参数,另一种定义预期行为。

我正在考虑的一个选项是使用注释来定义预期结果。然后我可以覆盖注释,然后将实现委托给基础 class 实现。只要 base class 使用注解来决定行为方式,它就应该可以工作。

例如:(在伪代码中,不是真实的Java)

abstract class foo {

  @fubar(true)
  void test1() { doSomething( param1 ) }

  @fubar(true)
  void test2() { doSomething( param2 ) }

  @fubar(false)
  void test3() { doSomething( param3 ) }

  boolean isFubar( void ) { ... }  // generic annotation grabber

  void doSomething( p ) {
    if ( isFubar() ) 
      doThis( p ) 
    else 
      doThat( p );
  }

  // abstract methods to be defined in the implementing class
  void doThis( p );
  void doThat( p );
}

class bar extends foo {

  // change the expected result for test2 only
  @fubar(false)
  void test2() { super.test2(); }

  // define how to execute the tests
  void doThis(p) { ... }
  void doThat(p) { ... }

}

class baz extends bar {

  // only changes the semantics of test3, nothing else
  @fubar(true)
  void test3() { super.test3() }

}

鉴于此层次结构,foo.test1()bar.test1()baz.test1() 都做完全相同的事情。而 foo.test2() 做一件事,而 bar.test2()baz.test2() 做另一件事。同样,foo.test3()bar.test3()做一件事,但baz.test3()会有所不同。

我可以使用注释来完成此行为吗?如果是这样,isFubar 会是什么样子?我还没有看到这种方法名称未知的反射示例。

附带说明一下,如果有更简洁的方法来完成预期的行为,我很乐意听听它是什么。

要在调用堆栈中找到最早匹配的注释,您可以使用这样的函数:

static <T extends Annotation> T seekAnnotation(Class<T> annotationClass) {
    T annotation = null;
    try {
        for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
            T found = seekAnnotation(annotationClass, ste);
            if (found != null) {
                annotation = found;
            }
        }
    } catch (Exception e) {
        //
    }
    return annotation;
}

static <T extends Annotation> T seekAnnotation(Class<T> annotationClass, StackTraceElement ste) {
    T annotation = null;
    try {
        Class<?> claz = Class.forName(ste.getClassName());
        Method method = claz.getDeclaredMethod(ste.getMethodName());
        annotation = method.getAnnotation(annotationClass);
    } catch (Exception e) {
        //
    }
    return annotation;
}

要让它更健壮还有很多工作要做,但这将是基本的想法。所以你的方法 isFubar() 看起来像

boolean isFubar() {
    return seekAnnotation(fubar.class).value();
}