问题 如何在一个方法中编写UT来模拟内部对象?


例如,我有一个java类,如下所示。我将为doWork()编写一个单元测试,所以我想控制obj的行为。但很明显,obj是在内部实例化的。

我怎么写这个UT?现在我正在使用Junit + Mockito。

class ToBeTest{
    public ToBeTest(){}
    public boolean doWork(){
        OtherObject obj=new OtherObject();
        return obj.work();
    }
}

提前致谢。 :)

顺便说一句,现实是我正在为其他人的班级写UT。所以我不想改变它。它已经过集成测试的全面测试。


1301
2017-09-16 06:27


起源

是否 obj  有 在内部实例化?相反,将它视为依赖是否有意义? - Jon Skeet
你是对的。但现实是我正在为其他人的班级写UT。所以我不想改变它。它已经过全面测试。 - Smartmarkey
还有一点。我们不能在课堂上注入所有对象。就像我们打开一个文件一样,我们需要实例化一个File类。 - Smartmarkey
您只能通过字节代码操作更改创建的类。单元测试最适用于考虑到测试而设计的功能。 ;) - Peter Lawrey
如果需要打开文件,可以(定义并创建然后)传入FileOpener对象,而不需要在对象中创建File对象。如果您愿意,可以始终通过传入创建对象的工厂来避免创建对象。 - Sam Holder


答案:


如果您无法更改代码,可以使用Powermock以及junit和Mockito来模拟新对象的构造。

@Test
public void testDoWork() throws Exception{

    MyTest mytest = new MyTest();

    OtherObj obj = new OtherObj();
    obj.test="mocked Test"; //here you can add any other needed values to obj

    PowerMockito.whenNew(OtherObj.class).withNoArguments().thenReturn(obj);

    String result = mytest.doWork();
    Assert.assertTrue(result.equalsIgnoreCase("mocked Test"));
}

10
2017-09-16 13:33



非常感谢。这正是我所需要的。我试了一下。太好了。 - Smartmarkey
我们可以在PowerMockito中返回一个模拟对象吗? whenNew 返回? - nishant


最好的方法是 编写代码以支持测试 (测试驱动开发 正在强调这一点)。目前,您的代码以难以测试的方式编写。

考虑使用依赖注入,因为它可以帮助你模拟依赖对象。


2
2017-09-16 07:46





你不能轻易。我可以想到两种方法可以做到这一点,据我所知,Mockito或jUnit都不支持开箱即用:

1)使用cglib或类似库的字节代码操作,这将是适度难以做到的并且可能非常脆弱。

2)备用类加载器。您可以构建一个类加载器,该类加载器查找尝试加载OtherObject类并将其替换为匿名的OtherObject类,该类提供您正在查找的模拟行为。

大多数情况下,你应该将它视为依赖。如果你想测试打开一个文件,你可能真的想用文件测试,所以使用具体的类可能没问题。如果你想测试一个方法的行为,即打开一个文件作为它逻辑的一部分,你可以轻松地将它移到一个依赖项然后模拟它。事实上,这通常是有道理的,因为你有一天存储在文件中的内容,可能需要存储在另一个数据库中,或者在第三天从云中下载,因此将逻辑与你对文件的处理分开无论如何,打开检索内容的实际过程通常是关注的逻辑分离。


1
2017-09-16 07:11





这是您应该使用的经典示例 依赖注入

简而言之,不是在内部创建对象(依赖),而是在构造函数中传递它或使用工厂来创建所需的东西(工厂在生产代码中返回实际实现,在测试中返回另一个)。这使您可以在测试时更改实现。

看看我提供的类似的例子或google for“Java依赖注入示例”。


1
2017-09-16 11:33





这很容易:

import org.junit.*;
import mockit.*;

@Test
public void justMockIt()
{
    new NonStrictExpectations() { OtherObject o; { o.work(); result = true; }};

    assert new ToBeTest().doWork();
}

......使用时 JMockit


1
2017-09-16 12:44





你已经编写了代码,现在你想对它进行单元测试。这是你遇到困难的根本原因。我建议采用不同的方法。

  1. 表达了什么 doWork() 方法意味着在可以观察到的行为方面做 只要 通过 public 和 protected (getter)方法 ToBeTest 上课,或者 public 和 protected 任何与之关联的对象的方法 ToBeTest 对象。看一下Java库提供的Javadoc:它描述了所有这些类的功能  陈述方法的主体。你的方法何时返回 true?什么时候回来 false?它有什么副作用?您可能会发现需要添加一些getter方法来执行此操作。你可以 在Javadoc中为您自己的代码表达这些
  2. 使用所需的行为来确定可以在单元测试中放置哪种断言。

0
2017-09-16 11:52