想象一下,你有一个应用程序,你想对它进行单元测试和功能测试(不是很难想象)。你可能有一个抽象类,我们称之为AbstractTestClass,你的所有单元测试都会从中扩展。
AbstractTestClass看起来像这样(使用JUnit 4):
class AbstractTestClass {
boolean setupDone = false;
@Before
public void before() {
if(!setupDone) {
// insert data in db
setupDone = true;
}
}
}
这就是我正在努力的方向。我有另一个测试Web界面的抽象类:
class AbstractWebTestClass extends WebTestCase {
boolean setupDone = false;
@Before
public void before() {
if(!setupDone) {
// here, make a call to AbstractTestClass.before()
// init the interfaces
setupDone = true;
}
// do some more thing
}
}
除了扩展WebTestCase之外,它几乎是同一个类。这种设计可以让我在单元测试时获得与测试接口时相同的数据。
通常,在处理此类问题时,您应该优先考虑组合而不是继承或使用策略模式。
不幸的是,我不喜欢在这个特定场景中支持组合而不是继承的想法,我不知道如何使用策略模式,可能存在设计缺陷,我无法看到溶液
我如何设计这个架构以实现我的目标。
答案 0 :(得分:2)
我将通过以下方式实现此目的:
class Example {
class LazyInitStrategy implements Runnable {
private final Runnable operation;
private boolean done = false;
LazyInitStrategy(Runnable operation) {
this.operation = operation;
}
@Override
public void run() {
if (!done) {
operation.run();
done = true;
}
}
}
private final class AbstractInit implements Runnable {
public void run() {
// insert data in db
}
}
private final class AbstractWebInit implements Runnable {
public void run() {
// here, make a call to AbstractTestClass.before() init the interfaces
}
}
class AbstractTestClass {
final LazyInitStrategy setup = new LazyInitStrategy(new AbstractInit());
@Before
public void before() {
setup.run();
// do some more thing
}
}
class AbstractWebTestClass extends WebTestCase {
final LazyInitStrategy setupInfo = new LazyInitStrategy(new AbstractWebInit());
@Before
public void before() {
setupInfo.run();
// do some more thing
}
}
}
当然这是一个非常简单的解决方案,但它应该消除if / else逻辑重复,以检查设置是否已完成。使用Runnable是可选的,我这样做是为了演示目的,在阅读世界中你可能会使用另一个界面。
答案 1 :(得分:0)
要完成的重要事情不是重复代码。在这种情况下,我会创建一个
MyScenarioTestUtil
类上有一堆静态方法,可以根据需要设置数据。您将从设置中调用实用程序方法。这样你就可以将所有代码保存在一个地方。
它真的只是与使用合成语言的语义差异......
答案 2 :(得分:0)
我认为一般来说设计是错误的。你根本不应该在单元测试中使用继承。测试应该是孤立的,非常简单。很多时候,就像你的情况一样,有必要准备一些补充对象,这将有助于测试方法来完成他们的工作。在这种情况下,您应该定义此类对象的构建器,并将它们放在测试用例之外的某处。
例如:
public void testMethodThatNeedsSomePreparedObjects() {
Foo foo = new FooBuilder()
.withFile("some-text.txt")
.withNumber(123)
.build();
// now we are testing class Bar, using object of class Foo
Bar bar = new Bar(foo);
}
因此,您需要在其他地方定义FooBuilder
,并且此类将执行您现在尝试使用状态模式或继承执行的所有工作。在处理单元测试时,两者都是错误的。