【问题标题】:How to mock object construction?如何模拟对象构造?
【发布时间】:2010-09-10 15:45:11
【问题描述】:

有没有办法在 Java 中使用 JMock 来模拟对象构造?

例如,如果我有这样的方法:

public Object createObject(String objectType) {
    if(objectType.equals("Integer") {
        return new Integer();
    } else if (objectType.equals("String") {
        return new String();
    }
}

...有没有办法在测试方法中模拟对象构造的期望?

我希望能够期望调用某些构造函数,而不是使用额外的代码来检查类型(因为它并不总是像我的示例那样复杂和简单)。

所以而不是:

assertTrue(a.createObject() instanceof Integer);

我可能期望调用某个构造函数。只是为了让它更简洁一些,并以更易读的方式表达实际测试的内容。

请原谅这个简单的例子,我正在处理的实际问题有点复杂,但是有期望会简化它。


更多背景知识:

我有一个简单的工厂方法,它创建包装对象。被包装的对象可能需要在测试类中难以获得的参数(它是预先存在的代码),因此很难构造它们。

也许更接近我真正想要的是:有没有一种方法可以一举模拟整个类(使用 CGLib),而不指定每个方法来存根?

所以模拟被包装在一个构造函数中,所以显然可以在其上调用方法,JMock 是否能够动态模拟出每个方法?

我的猜测是否定的,因为那会非常复杂。但是知道我在叫错树也很有价值:-)

【问题讨论】:

  • 您的工厂可以为它可能构建的每种类型(整数工厂、字符串工厂等)拥有自己的工厂,但是,您是对的,这会变得过于复杂。你断言 instanceof test 可能是最好的方法。

标签: java tdd junit mocking


【解决方案1】:

我唯一能想到的就是在工厂对象上使用 create 方法,而不是模拟。

但就模拟构造函数调用而言,没有。模拟对象以对象存在为前提,而构造函数以对象不存在为前提。至少在分配和初始化一起发生的java中。

【讨论】:

    【解决方案2】:

    jmockit 可以做到这一点。

    https://stackoverflow.com/questions/22697#93675查看我的回答

    【讨论】:

    【解决方案3】:

    唉,我觉得我问错了问题。

    我试图测试的简单工厂看起来像:

    public Wrapper wrapObject(Object toWrap) {
        if(toWrap instanceof ClassA) {
            return new Wrapper((ClassA) toWrap);
        } else if (toWrap instanceof ClassB) {
            return new Wrapper((ClassB) toWrap);
        } // etc
    
        else {
            return null;
        }
    }
    

    我问的是如何查找是否调用了“new ClassAWrapper()”,因为在孤立的测试中很难获得对象 toWrap。包装器(如果它甚至可以被称为)有点奇怪,因为它使用同一个类来包装不同的对象,只是使用不同的构造函数[1]。我怀疑如果我把这个问题问得更好一点,我很快就会得到答案:

    “您应该模拟 Object toWrap 以匹配您在不同测试方法中测试的实例,并检查生成的 Wrapper 对象以找到返回的正确类型......并希望您足够幸运,您没有'不必模拟世界来创建不同的实例;-)"

    我现在对眼前的问题有了一个好的解决方案,谢谢!

    [1] 提出是否应该重构的问题完全超出了我当前问题的范围 :-)

    【讨论】:

      【解决方案4】:

      你熟悉Dependency Injection吗?

      如果不是,那么您肯定会从了解该概念中受益。我想Martin Fowler 的经典Inversion of Control Containers and the Dependency Injection pattern 将作为一个很好的介绍。

      使用依赖注入 (DI),您将拥有一个 DI 容器对象,它能够为您创建各种类。然后,您的对象将使用 DI 容器来实例化类,并且您将模拟 DI 容器以测试该类是否创建了预期类的实例。

      【讨论】:

        【解决方案5】:

        依赖注入或控制反转。

        或者,对您创建的所有对象使用抽象工厂设计模式。当您处于单元测试模式时,注入一个测试工厂,它会告诉您正在创建什么,然后在测试工厂中包含断言代码以检查结果(控制反转)。

        为了让您的代码尽可能干净,请创建一个内部受保护接口,将生产代码作为内部类实现该接口(您的工厂)。添加初始化到默认工厂的接口的静态变量类型。为工厂添加静态设置器,您就完成了。

        在您的测试代码中(必须在同一个包中,否则内部接口必须是公共的),使用断言代码和测试代码创建一个匿名或内部类。然后在您的测试中,初始化目标类,分配(注入)测试工厂,并运行目标类的方法。

        【讨论】:

          【解决方案6】:

          我希望没有。 模拟应该模拟没有构造函数的接口......只有方法。

          您在此处进行测试的方法似乎有些不对劲。为什么需要测试是否调用了显式构造函数?
          断言返回对象的类型对于测试工厂实现似乎没问题。将 createObject 视为黑盒。检查它返回的内容,但不要对它的执行方式进行微观管理。没有人喜欢这样:)

          更新更新:哎呀!绝望时期的绝望措施是吗?如果 JMock 允许这样做,我会感到惊讶……正如我所说,它适用于接口……而不是具体类型。 所以

          • 要么尝试并花费一些精力来让那些讨厌的输入对象在测试工具下“可实例化”。采用自下而上的方法。
          • 如果这不可行,请使用断点手动测试它(我知道这很糟糕)。然后在源文件的可见区域中粘贴“触摸它,后果自负”评论并继续前进。再战一天。

          【讨论】:

          • 这是一个简单的工厂方法。问题是工厂为其他对象创建了包装器(在构造过程中将它们作为参数)并返回它们。我希望能够测试构造函数是否被调用,而不是模拟或实例化被包装的对象。
          • 问题在于工厂方法采用了一个很难单独重新创建的对象——重构以允许这样做现在几乎是不可能的。虽然......嘲笑那些可能是答案......
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-11-19
          • 1970-01-01
          • 2011-01-25
          • 2018-04-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多