我想定义一个抽象基类,它定义了我想在测试各种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
会是什么样的?我还没有看到这种反射的例子,其中方法名称是未知的。
作为旁注,如果有更清洁的方法来实现预期的行为,我会很高兴听到它是什么。
答案 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();
}