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

时间:2017-07-25 23:56:34

标签: java testing annotations automated-tests java-annotations

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

本质上,我正在进行参数化测试,其中参数在基类中定义。预期结果也在基类中定义。但是,有时需要覆盖预期结果。例如,普通字符串字段可能允许任何字符,而对象名称字段可能(或在我的情况下为DOES)限制哪些字符是合法的。例如,String字段允许“?”在值中,“名称”字段不会。

我正在尝试找出一种在基类中定义参数ONCE的简洁方法,并且只定义预期结果一次,但可以选择在上下文需要它的实现类中重写它。

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

我正在考虑的一个选项是使用注释来定义预期结果。然后我可以覆盖注释,然后将实现委托给基类实现。只要基类使用注释来决定如何表现,它就应该有效。

例如:(伪代码,不是真正的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会是什么样的?我还没有看到这种反射的例子,其中方法名称是未知的。

作为旁注,如果有更清洁的方法来实现预期的行为,我会很高兴听到它是什么。

1 个答案:

答案 0 :(得分:0)

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

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