2017-07-25 52 views
-1

我想定义一个抽象基类来定义通用可重用测试用例,我希望在测试各种API时一遍又一遍地执行这些测试用例。这个想法是定义一次测试,然后我可以从通用测试继承来获得测试定义,我所有的测试代码都要执行测试执行的具体细节。这样我就不会错过好的测试用例,我不需要重新发明轮子,也不需要在数十个地方进行更改,当我想要另一个好的测试用例进行测试时。从调用堆栈中检索最顶级注释的函数

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

我想弄清楚在基类中定义参数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

闻起来像一个可能的XY问题。您可能希望查看AOP,或尝试找到更多面向对象的方法。 – shmosel

+1

请详细说明*你想要完成什么,而不是*如何*。按照你的代码片断,简单地重写'isFubar()'返回true或false,并完全丢弃注释会更有意义。 – shmosel

+1

此外,您提出的问题是作为代码请求发生的,这是在此处皱起眉头。你有没有试图解决这个问题?你卡在哪里? – shmosel

回答

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

不幸的是,在这个例子中事物过于简单。无论哪种方法都有注释,“isFubar”的定义需要工作。因为在课堂上可能有50-100个有注释的方法。这就是我提到遍历调用堆栈的原因。 –

+0

这里是第二个版本,走栈跟踪。 –

+0

不错!它看起来像,除非它在循环时出错,它将采用调用堆栈中最高的注释值,这正是我想要的。它能够做到这一点,而不必知道具体的方法名称,这是我不清楚如何做。我将把这标记为我的问题的答案。 –