【问题标题】:How to call pass parameter that indicates what method should be called on instance如何调用指示应该在实例上调用什么方法的传递参数
【发布时间】:2020-07-18 15:32:50
【问题描述】:

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

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");

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

谢谢!

【问题讨论】:

    标签: java functional-interface


    【解决方案1】:

    一般性声明

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

    访客模式

    如果您真的想这样做,可以使用Visitor Design Pattern 来完成。

    假设你有两个这样的类:

    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);
    }
    

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

    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);
        }
    } 
    

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

    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");
    }
    

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-25
      • 1970-01-01
      • 1970-01-01
      • 2017-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-30
      相关资源
      最近更新 更多