你好,我的Java有点生疏,所以请耐心等待。我的任务是减少教授研究项目中重复代码的数量,所以我认为我不能在这里发布任何代码。但基本上我在不同的类中有大约20个通常相同的方法(测试方法)的情况,我一直在尝试两种不同的方法来解决这个问题,但我遇到了每个问题。< / p>
我发现减少重复的第一种方法是删除所有测试方法中包含的初始变量,并将它们放在一个单独的方法中( prepare method )在超类内部并将其调用测试方法。这个解决方案的问题不在于 prepare方法中声明的所有变量都将保持为本地,并且只要在另一个方法中调用该方法就会被删除吗?
我的第二个想法就是创建超类的所有变量字段并让它们由子类继承。这个解决方案几乎可以工作,除了创建许多变量所必需的变量IFile importedFile = importFile(file);
之一必须具有抛出异常所包含的内容,我不认为你可以用类做。
我希望有人可以用这些解决方案之一指出我正确的方向,或者可能建议我找不到另一种解决方案。
我忘了提到的一点是,除了初始变量之外,每种测试方法在测试方法编写方式上略有不同。 编辑:如果不是这样,我会把方法拉到超类中并完成它。
编辑:这是测试方法和我正在使用的超类的部分,
// method inside of the subclass
public void test() throws Exception {
// variables removed and placed in AbstractTest
for (int i = 0; i < expectedExitNodeCount; i++) {
if (markerFields.peekFirst().equalsIgnoreCase("EXPOSED_EXIT")) {
expectedExitNodes.add(new CTrueExitNode());
} else {
fromLine = Integer.parseInt(markerFields.removeFirst().trim());
fromCol = Integer.parseInt(markerFields.removeFirst().trim());
toLine = Integer.parseInt(markerFields.removeFirst().trim());
toCol = Integer.parseInt(markerFields.removeFirst().trim());
length = length(ast, fromLine, fromCol, toLine, toCol);
assertTrue(length > 0);
ICFlowNode expectedExit = findNode(ast, ICFlowNode.class, fromLine, fromCol, length);
assertNotNull(expectedExit);
expectedExitNodes.add(expectedExit);
}
}
// additional code omitted from method
}
// Superclass the variables have been placed in.
public abstract class AbstractTestCase extends WorkTest {
protected File file;
protected String markerText;
public AbstractTestCase(String name, VPG< ? , ? , ? > vpg) {
super(name, vpg);
}
protected void prepare() throws Exception {
// Variables used in the test method
IFile importedFile = importFile(file);
project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
CVPG.getInstance().ensureVPGIsUpToDate();
CTranslationUnit ast = CVPG.getInstance().acquireTransientAST(
ResourceUtil.getFilenameForIFile(importedFile));
LinkedList<String> markerFields = MarkerUtil.parseMarker(markerText);
int fromLine = Integer.parseInt(markerFields.removeFirst().trim());
int fromCol = Integer.parseInt(markerFields.removeFirst().trim());
int toLine = Integer.parseInt(markerFields.removeFirst().trim());
int toCol = Integer.parseInt(markerFields.removeFirst().trim());
int length = length(ast, fromLine, fromCol, toLine, toCol);
assertTrue(length > 0);
IASTNode node = findNode(ast, IASTNode.class, fromLine, fromCol, length);
assertNotNull(node);
Integer expectedExitNodeCount = Integer.parseInt(markerFields.removeFirst().trim());
Set<ICFlowNode> expectedExitNodes = new HashSet<ICFlowNode>();
}
protected void test() throws Exception {
//Considered making test a inherited method
}
}
答案 0 :(得分:1)
为什么现场方法不那么酷:你正在引入顺序耦合。接下来的任何人都不会轻易知道方法测试会在没有调用方法准备的情况下失败。此外,如果一个类扩展另一个或结构更改,您可能最终调用prepare两次并且难以跟踪错误
模式1)将所有变量移动到状态对象
将变量分组为新对象 - 而不是超类
创建一个构建器对象,以便您可以轻松地将所需的变量初始化为此对象
更改测试方法以接受此对象并从其数据
开始工作public class TestParameters {
public boolean flag1;
public int someNumber;
}
public class Tester {
public static void test(TestParameters p) {
for (int i=0; int i<p.someNumber;i++) {
if (p.flag1) doA();
else doB();
}
}
}
public class Builder {
TestParameters p = new TestParameters();
new Builder() {
}
public Builder setFlag(boolean f) {
p.flag1 = f;
}
public Builder setNumber(int n) {
p.someNumber = n;
}
public TestParameters build() {
return p;
}
}
public class SomeClass {
public void doSomething() {
TestParameters p = new Builder().setFlag(true).setNumber(10).build();
Tester.test(p);
}
}
模式2)控制反转
为您的测试方法创建一个类,创建这个新类的变量部分字段。在这个类构造函数中设置变量 - 添加静态getInstanceForXxxx方法并将每个这些静态getter所需的魔术值硬编码 - 每个getInstanceForXxxx你创建一个新的实例,传递客户需要的初始值
public class Tester {
public boolean flag1;
public int someNumber;
private Tester() {};
//feel free to be more descriptive if each initialization apply for more than one class
public static Tester testForClassSomeClass() {
Tester t = new Tester();
t.flag1=false;
t.int=2;
return t;
}
public void test() {
for (int i=0; int i<someNumber;i++) {
if (flag1) doA();
else doB();
}
}
}
public class SomeClass {
public void doSomething() {
Tester t = Tester.testForClassSomeClass();
t.test();
}
}
模式3)封装策略中的行为
创建基类。重复代码的每个点都是不同的,在私有方法中重构。创建一个类并使用您需要的专用代码覆盖每个方法。为每个客户提供正确的课程并调用公共测试方法
public abstract class Tester {
private Tester() {};
public boolean getFlag();
public int getNumber();
public int someLogic();
public void test() {...}
}
public class SomeClassTester {
private Tester() {};
public int getNumber() {
return false;
}
public int someLogic() {
doA();
}
public static void test() {
for (int i=0; int i<getNumber();i++) {
doA();
}
}
}
public class SomeClass {
public void doSomething() {
Tester t = new SomeClassTester();
t.test();
}
}
我最后喜欢它,因为它摆脱了一个标志(flag1)并封装了正确的行为(特定的SomeClass测试只有doA()调用)
答案 1 :(得分:0)
我认为将变量定义为字段是一种好方法。但是你应该稍后初始化它们。只需使用构造函数来初始化变量,就可以捕获异常!