首页 > 解决方案 > 如何调用指示应该在实例上调用什么方法的传递参数

问题描述

在我的测试中,我有很多类似的东西:

try{
  someInstance.someMethod(argument)
}catch(Exception e){
  // assert exception message
}

我想为此创建方法,但是如何?我如何传递应该在特定实例上调用的方法?

我试过了

private <String,R> void  runCommandWithExpectedError(Function<String,R> fnc, String argument, String errorMessage){
    try {
        fnc.apply(argument);
    } catch (Exception e) {
        Assert.assertThat(e.getMessage(), Matchers.equalTo(errorMessage));
    }
}

但是它抱怨不能从静态上下文中引用非静态方法。像这样称呼它时

runCommandWithExpectedError(ChannelSftp::rm,"path","No such file or directory");

此外,该方法无法知道它应该在哪个实例上调用该方法,这样可能吗?

谢谢!

标签: javafunctional-interface

解决方案


一般性发言

我不会这样做来简化我的单元测试。单元测试应该测试代码的每个组件,并且应该易于阅读和修改。让它们保持这样,即使它们听起来重复,这就是你的代码的工作方式,我希望有一个简单的测试,它看起来像所有其他的,但很容易显示它的作用。

访客模式

如果您真的想这样做,可以使用访问者设计模式来完成。

想象一下,你有两个这样的类:

public class Class1 {
    public int method1(String string) {
        return string.contains("something") ? 2 : 3;
    }
}

public class Class2 {
    public double method2(String string) {
        return string.contains("something") ? 3.0 : 4.0;
    }
}

您的测试如下所示:

@Test
public void test1() {
    Class1 class1 = new Class1();
    int result = class1.method1("something");
    //...assertions
}

@Test
public void test2() {
    Class2 class2 = new Class2();
    double result = class2.method2("else");
    //...assertions
}

如果我的理解是正确的,您宁愿使用单个方法测试对该方法的调用并最终捕获异常以测试其消息。


Visitor首先,为要测试的每种类型创建一个接口:

public interface Visitor {
    int visit(Class1 class1, String argument);
    double visit(Class2 class2, String argument);
}

然后,您创建一个Visitable接受Visitor类型的接口:

public interface Visitable {
    public void accept(Visitor visitor, String argument);
}

一旦你这样做了,你应该让你想要测试的类实现Visitable接口(基本上让它们被“访问者”“访问”):

public class Class1 implements Visitable {
    public int method1(String string) {
        return string.contains("something") ? 2 : 3;
    }

    @Override
    public void accept(Visitor visitor, String argument) {
        visitor.visit(this, argument);
    }
}

public class Class2 implements Visitable {
    public double method2(String string) {
        return string.contains("something") ? 3.0 : 4.0;
    }

    @Override
    public void accept(Visitor visitor, String argument) {
        visitor.visit(this, argument);
    }
} 

现在,您可以创建一个TestVisitor实现Visitor接口并执行您希望的操作的类:

public class TestVisitor implements Visitor {

    @Override
    public int visit(Class1 class1, String argument) {
        return class1.method1(argument);
    }

    @Override
    public double visit(Class2 class2, String argument) {
        return class2.method2(argument);
    }
}

因此,可以通过执行操作的单个测试来重构您的单元测试:

private final Visitor TEST_VISITOR = new TestVisitor();

private void sharedTestMethod(Visitable instance, String argument) {
    instance.accept(TEST_VISITOR, argument);
}

您的测试只需创建实例然后调用sharedTestMethod

@Test
public void test1() {
    Class1 class1 = new Class1();
    sharedTestMethod(class1, "something");
}

@Test
public void test2() {
    Class2 class2 = new Class2();
    sharedTestMethod(class2, "else");
}

我们不要忘记缺点(由上面的链接引用):

注意缺点 访问方法的参数和返回类型需要提前知道,因此访问者模式不适用于这些访问的类可能发生变化的情况。每次添加新类型的元素时,都必须修改每个访问者派生类。此外,将访问者模式重构为尚未考虑到该模式的代码可能很困难。而且,当您添加访问者代码时,它可能看起来晦涩难懂。访客功能强大,但您应确保仅在必要时使用它。


推荐阅读