【问题标题】:Java Unit Testing - Mock all class methods to return one resultJava 单元测试 - 模拟所有类方法以返回一个结果
【发布时间】:2020-04-03 17:42:26
【问题描述】:

我想模拟一个类上的所有方法以返回一个结果,而不是分别模拟每个方法,例如:

mock(Foo.class, allMethodsWithAnyArgs).thenReturn('abc');

我对 Java 测试很陌生,所以请原谅这个愚蠢的问题。这个巨大的项目有 Mockito、PowerMockito、PowerMock、EasyMock,可能还有其他东西,所以我可以使用任何推荐的东西。

一个扩展的例子是这样的,我需要测试 Foo:

public class Foo {
    doSomething(){
        Bar bar = new bar();

        // some code

        bar.x(a);

        // some code

        bar.x(a, b, c);

        // some more code

        bar.y(...);

        // even more code

        bar.z(...);
    }
}

我需要存根所有的 bar 调用,但不关心返回,或者我想要相同的返回,比如 1 或来自所有调用的“ok”,等等。

更新: 我同意这不是最好的测试代码,这是我在这种情况下尝试测试的更新示例,所以我需要存根 MyUtil 和 bar 方法,这些是静态的,所以你建议我如何重构他们?将 MyUtil 作为必需参数对我来说没有意义,如果可能的话,我更愿意将 Foo 保持为静态。一个有效的测试文件将非常感谢,因为我这几天一直在努力解决这种问题。

public class Foo {
    public static Object doSomething(Long id){
        Bar bar = new Bar();
        Object obj;

        if(somecondition) {
            Long vnumber = MyUtil.getVNumber(id);

            obj = bar.getCurrentObj(id, vnumber);            
        } else {
            obj = bar.getPreviousObj(id);
        }

        bar.z(obj);
        ....
    }
}

另外,这个类的一个要点是让它成为一个黑盒子,我不希望被调用者担心创建实例和传递它们,甚至知道事情是如何工作的,我只想传递一个ID 并得到它的结果。例如,某些调用函数甚至无法访问 Bar,因此我无法将其设为必需参数。

【问题讨论】:

  • 我不知道这种方法,但你为什么需要这个?
  • 例如,如果我有一个重载的方法或在我正在测试的文件中,我会从存根类中调用几个方法,但我不关心结果。
  • I need to stub all the bar calls but don't care about the return 这对我来说似乎很矛盾。如果您不关心结果,那么您无需做任何事情。
  • 其中一些是数据库调用/对其他函数的调用,我不希望它们通过但我只想返回一个 1 例如,其中一些与测试用例无关因为如果我存根最后一步的结果,那么第一个函数返回的内容并不重要。然而,它们仍然需要被存根以保持测试本地化/避免外部调用。
  • “将 MyUtil 作为参数是没有意义的” - 相反,它被称为控制反转,它是 SOLID 原则的一部分。它使您可以控制您的类依赖关系,因此您将能够正确地模拟它们。让它们保持静态也是一个坏主意,因为您将无法轻松更改或模拟它

标签: java unit-testing testing


【解决方案1】:

有这么多要模拟的方法可能表明一个类有太多职责。

但是,模拟并不是唯一的 test doubles。 fake 可能更适合您的用例。

这实际上取决于Foo 是否在接口后面:

interface Bar {


   String x(String s);


   String y(String s);
}

那么你就有了你的产品实现:

class RealBar implements Bar {

    @Override
    String x(String s) {
        // whatever your production logic is
    }
}

然后是您在测试中使用的假货,它位于您的 test 源集中:

class FakeBar implements Bar {

    @Override
    String x(String s) {
        return "abc";
    }
}

因为伪造的作品开箱即用,您可以重复使用它,而不必手动为它存根行为。

如果您在Foo 中使用构造函数注入,您的测试将包括传入您的假Bar 而不是生产Bar

@Before
public void setUp() {
   foo = new Foo(new FakeBar());
}

因此,伪造是避免手动存根负担的公认方式。

但是,如果您别无选择,只能将类的每个方法都存根以返回相同的值,那么这不是一个常见的用例,并且大多数模拟库不太可能支持。尤其是当 Mockito 试图在类设计上固执己见以防止您编写次优代码时。

您可以手动编写代码,使用反射来查找声明的方法,然后存根该行为。到你这样做的时候,还不如执行了“提取接口”,写了一个假的。

【讨论】:

  • 嗨大卫,感谢您的回复,我在这里测试的类是 statc 并使用静态 util 函数进行计算,将 util 类作为构造函数(特别是如果我使用多个),请参阅更新示例。
  • 不错的答案(终于有人真正了解测试替身了!)-我会删除@NonNull,因为它们与问题不太相关并会增加很多噪音
猜你喜欢
  • 1970-01-01
  • 2021-07-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多