7

我真的进入了 TDD,并且开始在 jUnit 中使用 mockito 来提高我测试代码的能力。我真的很喜欢模仿!

我注意到我必须改变我对编码的看法,比如尽可能多地将协作者传递给方法,并尽可能限制在构造函数中完成的工作。

以下情况需要专家们提供一些关于 SO 的建议。

假设我有一个方法,它将在某个类上调用一些静态方法。例如

public void method(){
    OtherClass.staticMethod();
}

这通常很糟糕,但在我的场景中是必需的。为了使代码在我的单元测试中更具可测试性,我想避免对 OtherClass 的依赖并将其作为参数传递。

这不起作用,因为它会产生编译时错误。

public void method(Class<? extends OtherClass> util){
    util.staticMethod();
}
...
method(OtherClass.class);

这会起作用,但如果我不需要,我不喜欢实例化 OtherClass ,因为它只是一类静态实用程序,如方法:

public void method(OtherClass util){
     util.staticMethod();
}
...
method(new OtherClass());

我的问题是:有没有更好更可取的方法来完成此操作而不使用 new 关键字?

4

7 回答 7

1

这会起作用,但如果我不需要,我不喜欢实例化 OtherClass ,因为它只是一类静态实用程序,如方法:

public void method(OtherClass util){
     util.staticMethod();
}
...
method(new OtherClass());

实际上,这不起作用,因为它总是会从 OtherClass 调用方法实现,而不管您传递的对象是什么(即使您传递了null)。

我强烈建议不要仅仅为了简化测试而使用反射,因为这会绕过编译时检查(编译器不会检测到拼写错误的方法名称),并且会阻止使用 IDE 的许多功能(代码完成、javadoc 悬停、重构支持,调用层次结构显示,跳转到定义,...)

常见的方法是使用多态调度。在 Java 中,这要求方法不是静态的,也不是私有的。因此,经验法则是:如果它需要模拟,它不应该是静态的。

如何最好地获取对象实例取决于您的情况;依赖注入(您的方法)、资源定位器模式和单例模式各有优缺点。

于 2013-05-09T11:33:44.703 回答
0

这里和那里的一些静态代码没有错 - 如果它不访问全局状态:

如果静态代码是纯程序性的

public void method(){
    OtherClass.staticMethod();
}

您可以在 method() 周围模拟它,只要您将静态调用作为方法中的唯一操作

否则,如果您的静态方法保护某些全局状态,则最好使用 Singleton+Locator

public class Registry {
    DoSomethingInterface getOtherClass() {
        return OtherClass.getSingleton();
    }
}


public void method(Registry reg){
    reg.getOtherClass().doSomething();
} 

然后,您可以将注册表子类化以向 getOtherClass() 定位器提供各种变化,因此您可以在测试时实例化一个 TestRegistry 以提供干净的全局状态和其他事情

如果您仍然需要其他类是静态的,因为您无法以任何方式更改它(即库类),您可以将其包装在您的界面中:

public class Registry {
    DoSomethingInterface getOtherClass() {
        return new DoSomethingInterface(){
            public void doSomething() {
                OtherClass.staticMethod();
            }
        };
    }
}
于 2013-05-09T11:10:27.893 回答
0

在您的代码示例中:

public void method(){
    OtherClass.staticMethod();
}

我的理解是,除了静态方法调用之外,上面的方法中应该有一些逻辑,那就是你要测试的,而不是静态方法。

如果是这样,在这种情况下,您可以编写模拟实现,OtherClass.staticMethod该实现将绕过所有逻辑或返回所需值或实现特定逻辑。这将避免依赖OtherClass并让测试对其进行显式控制。

于 2013-05-06T06:55:11.390 回答
0

您可以通过像这样更改您的方法来使用您的第一种方法

public void method(Class<? extends OtherClass> clazz) throws Exception {
    Method[] mArray = clazz.getMethods();
    for(Method m :mArray) {
        if(!m.isAccessible()) m.setAccessible(true);
        if((m.getModifiers() & Modifier.STATIC) != 0) { // Here a method which is static is executed. You can change this condition, to suit your needs
            m.invoke(null);
        }
    }
}

你可以通过调用来调用它

method(OtherClass.class);

不传递该类的新对象

于 2013-05-06T07:11:20.403 回答
0

这是我刚刚尝试过的:

public void testMethod(Class<T> clazz) throws Exception{        
        Method m = clazz.getMethod("staticMethod",null);
        m.invoke(null,null);
    }

我假设(如您的示例中所述)静态方法不接受任何参数。

请参考getMethod()inovke()方法的文档。

于 2013-05-06T07:33:16.413 回答
0

你可以使用反射:

// exceptions management omitted
public void method(Class<? extends OtherClass> clazz, String methodName) {
    // methodName could also be a constant if it won't change
    clazz.getMethod(methodName).invoke(null);
}

然后,

method(OtherClass.class, "staticMethod");
于 2013-05-06T07:22:03.110 回答
0

我不确定这是否有帮助。但我不得不做类似的事情,虽然不是为了测试目的。这就是我想出的。

创建一个一旦实例化的类只有一个方法,它调用另一个类的静态方法。如果您想进一步解耦它,请创建一个接口。就像是:

public interface MethodCaller {
    void callMethod();
}

然后使用 this 的一个实例作为您的方法的参数。

public void method(MethodCaller caller){
    caller.callMethod();
}

然后调用它。

method(new MethodCaller() {
   @Override
   public void callMethod() {
       OtherClass.staticMethod();
   }
});

希望这可以帮助!

于 2018-04-18T09:21:28.740 回答