我还有最后一节课,大概是这样的:

public final class RainOnTrees{

   public void startRain(){

        // some code here
   }
}

我在其他一些类中使用这个类,像这样:

public class Seasons{

   RainOnTrees rain = new RainOnTrees();

   public void findSeasonAndRain(){

        rain.startRain();

    }
}

在我的JUnit测试类Seasons.java中,我想模拟RainOnTrees类。我怎么能用Mockito做到这一点?


当前回答

实际上有一种方法,我用来监视的。只有满足两个前提条件,它才会为你工作:

使用某种DI注入final类的实例 Final类实现了一个接口

请回顾Effective Java中的第16项。你可以创建一个包装器(不是final)并将所有调用转发给final类的实例:

public final class RainOnTrees implement IRainOnTrees {
    @Override public void startRain() { // some code here }
}

public class RainOnTreesWrapper implement IRainOnTrees {
    private IRainOnTrees delegate;
    public RainOnTreesWrapper(IRainOnTrees delegate) {this.delegate = delegate;}
    @Override public void startRain() { delegate.startRain(); }
}

现在你不仅可以模仿你的最后一个类,还可以监视它:

public class Seasons{
    RainOnTrees rain;
    public Seasons(IRainOnTrees rain) { this.rain = rain; };
    public void findSeasonAndRain(){
        rain.startRain();
   }
}

IRainOnTrees rain = spy(new RainOnTreesWrapper(new RainOnTrees()) // or mock(IRainOnTrees.class)
doNothing().when(rain).startRain();
new Seasons(rain).findSeasonAndRain();

其他回答

如果你正在使用Mockito2,这可以做到,新的孵化功能支持模拟最终类和方法。

需要注意的要点: 1. 创建一个名为“org.mockito.plugins”的简单文件。并将其放在名为mockito-extensions的文件夹中。这个文件夹应该在类路径上可用。 2. 上面创建的文件内容应该是一行,如下所示: mock-maker-inline

为了激活mockito扩展机制并使用此选择加入特性,需要执行上述两个步骤。

示例类如下:-

FinalClass.java

public final class FinalClass {

public final String hello(){
    System.out.println("Final class says Hello!!!");
    return "0";
}

}

Foo.java

public class Foo {

public String executeFinal(FinalClass finalClass){
    return finalClass.hello();
}

}

FooTest.java

public class FooTest {

@Test
public void testFinalClass(){
    // Instantiate the class under test.
    Foo foo = new Foo();

    // Instantiate the external dependency
    FinalClass realFinalClass = new FinalClass();

    // Create mock object for the final class. 
    FinalClass mockedFinalClass = mock(FinalClass.class);

    // Provide stub for mocked object.
    when(mockedFinalClass.hello()).thenReturn("1");

    // assert
    assertEquals("0", foo.executeFinal(realFinalClass));
    assertEquals("1", foo.executeFinal(mockedFinalClass));

}

}

希望能有所帮助。

完整的文章呈现在这里嘲笑不可嘲笑的。

如果你需要在Android的测试中使用Mockito(即在Android设备中运行),你不能使用Mockito -inline。有一个特殊的模拟android版本也没有解决“最终类”的问题。唯一可行的解决方案似乎是Dexmaker库。唯一的限制是它只能在Android P (Android 9, API 28)或更高版本上运行。导入方式如下:

androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.1"

注意,还有一个“dexmaker-mockito”版本,它也不适用于final类。确保您导入了“dexmaker-mockito-inline”。

只有在Mockito v2中才可以模拟final/static类/方法。

添加到你的gradle文件:

testImplementation 'org.mockito:mockito-inline:2.13.0'

这是不可能与Mockito v1,从Mockito常见问题:

Mockito的局限性是什么 需要java1.5 + 不能模拟最终类 ...

在某些情况下可能适用的另一种解决方法是创建一个由最终类实现的接口,更改代码以使用该接口而不是具体类,然后模拟该接口。这样可以将契约(接口)与实现(最终类)分离。当然,如果您真正想要的是绑定到最终的类,这将不适用。

你不能用Mockito模拟最后一个类,因为你自己不能这样做。

我所做的是创建一个非final类来包装final类并作为委托使用。一个例子是TwitterFactory类,这是我的mockable类:

public class TwitterFactory {

    private final twitter4j.TwitterFactory factory;

    public TwitterFactory() {
        factory = new twitter4j.TwitterFactory();
    }

    public Twitter getInstance(User user) {
        return factory.getInstance(accessToken(user));
    }

    private AccessToken accessToken(User user) {
        return new AccessToken(user.getAccessToken(), user.getAccessTokenSecret());
    }

    public Twitter getInstance() {
        return factory.getInstance();
    }
}

缺点是有很多样板代码;好处是您可以添加一些可能与您的应用程序业务相关的方法(如在上面的例子中,getInstance接受用户而不是accessToken)。

在你的例子中,我会创建一个非最终的RainOnTrees类,它委托给最终的类。或者,如果你能让它变成非最终结果,那就更好了。