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

Java 如何在一个方法中编写UT来模拟内部对象?,java,unit-testing,Java,Unit Testing,例如,我有一个java类,如下所示。我将为doWork()编写一个单元测试,因此我希望控制obj的行为。但很明显,obj是在内部实例化的 我怎么写这个?现在我正在使用Junit+Mockito class ToBeTest{ public ToBeTest(){} public boolean doWork(){ OtherObject obj=new OtherObject(); return obj.work(); } } 提前感谢。

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

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

class ToBeTest{
    public ToBeTest(){}
    public boolean doWork(){
        OtherObject obj=new OtherObject();
        return obj.work();
    }
}
提前感谢。:)


顺便说一句,事实是我在为别人的课写UT。所以我不想改变它。它已经通过集成测试进行了全面测试。

您不可能很容易做到这一点。我可以想出两种方法来实现这一点,就我所知,Mockito或jUnit都不支持这两种方法:

1) 使用cglib或类似的库进行字节码操作,这将是相当困难的,而且可能非常脆弱

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


大多数时候,你应该把它当作一种依赖。如果您想要测试打开一个文件,您可能实际上想要使用一个文件进行测试,因此使用具体类可能是好的。如果您想测试一个方法的行为,将打开一个文件作为其逻辑的一部分,您可以很容易地将其移动到依赖项,然后模拟它。事实上,这通常是有意义的,因为您某天存储在文件中的内容,可能需要另一天存储在数据库中,或者在第三天从云中取出,因此,将文件处理的逻辑与打开文件检索内容的实际过程分离通常是一种逻辑上的关注点分离。

最好的方法是编写代码以支持测试(强调这一点)。目前,您的代码的编写方式很难进行测试


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

这是一个经典的示例,您应该使用它

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


请看下面的示例,如我为“Java依赖项注入示例”提供的示例或google提供的示例。

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

  • 通过
    ToBeTest
    类的
    public
    protected
    (getter)方法,表达
    doWork()
    方法仅能观察到的行为,或者与
    ToBeTest
    对象关联的任何对象的
    public
    protected
    方法。看一看Java库提供的Javadoc:它描述了所有这些类在不说明方法体的情况下所做的事情。yor方法何时返回
    true
    ?何时返回
    false
    ?它有什么副作用?您可能会发现需要添加一些getter方法来实现这一点。你可以
  • 使用所需的行为来决定可以在单元测试中放置哪些类型的断言
  • 这很简单:

    import org.junit.*;
    import mockit.*;
    
    @Test
    public void justMockIt()
    {
        new NonStrictExpectations() { OtherObject o; { o.work(); result = true; }};
    
        assert new ToBeTest().doWork();
    }
    

    。。。使用.

    时,如果无法更改代码,可以使用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"));
    }
    

    obj
    是否必须在内部实例化?将其视为依赖关系有意义吗?你是对的。但现实是我在为别人的班级写UT。所以我不想改变它。它已经被充分测试过了。还有一点。我们不能在类中注入所有对象。就像我们打开一个文件一样,我们需要实例化一个文件类。您只能更改通过字节码操作创建的类。单元测试最适合于在设计时考虑测试的功能。;)如果需要打开文件,可以(定义、创建然后)传入FileOpener对象,而不需要在对象中创建file对象。如果你愿意,你可以通过进入一个创建对象的工厂来避免创建对象。非常感谢。这正是我需要的。我试过了。很好。我们可以在PowerMockito的
    中返回一个模拟对象吗?