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

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做到这一点?


当前回答

由RC和Luigi R. Viggiano共同提供的解决方案可能是最好的主意。

尽管Mockito在设计上不能模拟final类,但是委托方法是可能的。这有它的优点:

如果API一开始就打算将类改为非final类(final类有其好处),那么不必强制将类改为非final类。 您正在测试API周围装饰的可能性。

在您的测试用例中,您故意将调用转发到被测试的系统。因此,通过设计,你的装饰什么都不做。

因此,测试还可以证明用户只能修饰API,而不能扩展API。

从更主观的角度来看: 我更喜欢将框架保持在最低限度,这就是为什么JUnit和Mockito通常对我来说就足够了。事实上,以这种方式限制有时也会迫使我进行重构。

其他回答

如果你需要在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”。

没有尝试final,但私有,使用反射删除修饰符工作!已经进一步检查,它不为最终工作。

使用Powermock。这个链接展示了如何做到这一点:https://github.com/jayway/powermock/wiki/MockFinal

我正在编写我在Java 11中模拟final/private类及其方法的各种失败尝试之后所遵循的步骤,这些尝试最终对我有用。

在里面创建一个名为org.mockito.plugins.MockMaker的文件 您的test/resources/mockito-extensions文件夹。请创建 Mockito-extensions文件夹(如果还没有)。 在上面的org.mockito.plugins.MockMaker文件中添加一行mock-maker-inline内容 添加

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*", "jdk.internal.reflect.*", "com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "org.w3c.*"})
@PrepareForTest(Utility.class)

类级别的注释。

在测试类中设置过程

  @Before
  public void setup () {
    MockitoAnnotations.initMocks(this);
    Mockito.mockStatic(ClassToBeMocked.class); 
  }

使用Mockito.when(..).thenReturn(..)进行断言 在多个测试用例的情况下,添加下面的代码

  @After
  public void after() {
        Mockito.framework().clearInlineMocks();
  }

我正在使用的模拟版本:3.9.0 Java版本:11

你不能用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类,它委托给最终的类。或者,如果你能让它变成非最终结果,那就更好了。