如何用mockito模拟最后一堂课

but*_*ski 169 java junit mockito

我有一个最后的课,类似这样:

public final class RainOnTrees{

   public void startRain(){

        // some code here
   }
}
Run Code Online (Sandbox Code Playgroud)

我在其他类中使用此类,如下所示:

public class Seasons{

   RainOnTrees rain = new RainOnTrees();

   public void findSeasonAndRain(){

        rain.startRain();

    }
}
Run Code Online (Sandbox Code Playgroud)

在我的JUnit测试类中,Seasons.java我想模拟这个RainOnTrees类.我怎么能和Mockito一起做这件事?

Win*_*der 170

Mockito 2现在支持最终的类和方法!

但就目前而言,这是一个"孵化"功能.它需要一些步骤来激活它,这在Mockito 2的新功能中有所描述:

最终类和方法的模拟是一种孵化,选择加入的功能.它使用Java代理程序检测和子类化的组合,以实现这些类型的可模拟性.由于这与我们当前的机制不同,并且这个机制具有不同的限制,并且由于我们希望收集经验和用户反馈,因此必须明确激活此功能以使其可用; 它可以通过mockito扩展机制通过创建src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker包含单行的文件来完成:

mock-maker-inline
Run Code Online (Sandbox Code Playgroud)

创建此文件后,Mockito将自动使用此新引擎,并且可以执行以下操作:

 final class FinalClass {
   final String finalMethod() { return "something"; }
 }

 FinalClass concrete = new FinalClass(); 

 FinalClass mock = mock(FinalClass.class);
 given(mock.finalMethod()).willReturn("not anymore");

 assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());
Run Code Online (Sandbox Code Playgroud)

在随后的里程碑中,团队将采用以编程方式使用此功能.我们将为所有不可挽救的场景确定并提供支持.请继续关注,请告诉我们您对此功能的看法!

  • 我仍然得到一个错误:**不能模拟/间谍类an​​droid.content.ComponentName Mockito不能模拟/间谍因为: - 最终类** (13认同)
  • 即使遵循上述提法,我也会得到错误:Mockito无法模拟/间谍,因为: - 最后一堂课 (7认同)
  • @vCillusion这个答案与PowerMock无关. (7认同)
  • 我遵循了这些指示,但我仍然无法完成这项工作,是否有人必须做其他事情? (6认同)
  • 确保将`org.mockito.plugins.MockMaker`文件放在正确的文件夹中. (3认同)
  • 制造商确保您具有模仿版本2.XX (2认同)
  • 确保`mock-maker-inline`是`src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker`中的单行。 (2认同)

小智 116

仅使用Mockito v2可以模拟最终/静态类/方法.

使用Mockito常见问题解答中的 Mockito v1是不可能的:

Mockito有什么限制

  • 需要java 1.5+

  • 不能模拟最后的课程

...

  • 这对我来说还不够。我还必须按照 https://www.baeldung.com/mockito-final 创建带有“mock-maker-inline”的 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker (12认同)
  • 这在 Scala 中对我不起作用(经过 sbt 修改)。 (2认同)

Lui*_*ano 41

你不能用Mockito嘲笑最后一堂课,因为你不能自己做.

我所做的是创建一个非final类来包装最终类并用作委托.这方面的一个例子是TwitterFactory类,这是我的可模拟类:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

缺点是有很多样板代码; 优点是您可以添加一些可能与您的应用程序业务相关的方法(例如,在上述情况下,用户而不是accessToken的getInstance).

在你的情况下,我将创建一个RainOnTrees委托给最终类的非final类.或者,如果你能使它成为非最终的,那就更好了.

  • +1.如果需要,你可以使用像Lombok的`@ Delegate`这样的东西来处理很多样板. (6认同)
  • @luigi可以为Junit添加代码片段作为示例.我尝试为我的最后一堂课创建Wrapper,但是无法完成,如何测试它. (2认同)

Ben*_*nyP 27

将其添加到您的gradle文件中:

testImplementation 'org.mockito:mockito-inline:2.13.0'
Run Code Online (Sandbox Code Playgroud)

这是使mockito与最终类一起工作的配置

  • 这会导致在 Linux / OpenJDK 1.8 上运行时出错:`org.mockito.exceptions.base.MockitoInitializationException:无法初始化内联字节好友模拟制作器。(Android 不支持此模拟制作器。)` (3认同)
  • 现在可能应该使用“testImplementation”而不是“testCompile”。Gradle 不再喜欢“testCompile”。 (2认同)

Gáb*_*ták 24

使用Powermock.此链接显示,如何操作:https://github.com/jayway/powermock/wiki/MockFinal

  • 我认为PowerMock就像那些应该只出现在"处方"基础上的药物之一.从某种意义上讲:人们应该清楚地知道PowerMock有很多问题; 使用它就像是最后的最后手段; 并应尽可能避免. (24认同)
  • 现在,您可以使用 Mockito 完成所有工作,而无需对依赖项使用 Power 模拟。 (2认同)

Mic*_*ang 15

只是为了跟进.请将此行添加到您的gradle文件中:

testCompile group: 'org.mockito', name: 'mockito-inline', version: '2.8.9'
Run Code Online (Sandbox Code Playgroud)

我尝试了各种版本的mockito-core和mockito-all.它们都不起作用.


Sat*_*tar 14

在 Mockito 3 及更多版本中,我遇到了同样的问题并从此链接修复了它

使用 Mockito 模拟最终类和方法 如下

在 Mockito 可以用于模拟 final 类和方法之前,需要对其进行 > 配置。

我们需要在项目的 src/test/resources/mockito-extensions 目录下添加一个文本文件,名为 org.mockito.plugins.MockMaker 并添加一行文本:

mock-maker-inline
Run Code Online (Sandbox Code Playgroud)

Mockito 在加载时检查扩展目录中的配置文件。该文件允许模拟最终方法和类。


Flá*_*ria 11

我猜你是final因为你想阻止其他类扩展RainOnTrees.正如Effective Java建议的那样(第15项),还有另一种方法可以让一个类关闭扩展而不进行扩展final:

  1. 删除final关键字;

  2. 制作它的构造函数private.没有类可以扩展它,因为它无法调用super构造函数;

  3. 创建一个静态工厂方法来实例化您的类.

    // No more final keyword here.
    public class RainOnTrees {
    
        public static RainOnTrees newInstance() {
            return new RainOnTrees();
        }
    
    
        private RainOnTrees() {
            // Private constructor.
        }
    
        public void startRain() {
    
            // some code here
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

通过使用此策略,您将能够使用Mockito并使用很少的样板代码关闭扩展类.


use*_*457 8

我有同样的问题.由于我试图模拟的类是一个简单的类,我只是创建了它的一个实例并返回它.

  • 当然,为什么要嘲笑一个简单的类?模拟用于"昂贵"的交互:其他服务,引擎,数据类等. (2认同)
  • 如果您创建了该实例,则之后无法在其上应用Mockito.verify方法.模拟的主要用途是能够测试它的一些方法. (2认同)

sak*_*das 6

尝试一下:

Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable));
Run Code Online (Sandbox Code Playgroud)

它对我有用."SomeMockableType.class"是您想要模拟或间谍的父类,而someInstanceThatIsNotMockableOrSpyable是您要模拟或间谍的实际类.

有关详细信息,请查看此处

  • 应该注意的是,代表与本地间谍嘲笑非常不同.在本机mockito间谍中,"this"在实例中引用了间谍本身(因为是使用子类)但是,在委托中,"this"将是真实对象someInstanceThatIsNotMockableOrSpyable.不是间谍.因此,无法对自调用函数进行返回/验证. (2认同)

Oze*_*tee 6

为在 Android + Kotlin 上遇到相同问题(Mockito + Final Class)的人节省时间。在 Kotlin 中,类默认是 final 的。我在一个带有架构组件的 Google Android 示例中找到了一个解决方案。从这里选择的解决方案:https : //github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample

创建以下注释:

/**
 * This annotation allows us to open some classes for mocking purposes while they are final in
 * release builds.
 */
@Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class OpenClass

/**
 * Annotate a class with [OpenForTesting] if you want it to be extendable in debug builds.
 */
@OpenClass
@Target(AnnotationTarget.CLASS)
annotation class OpenForTesting
Run Code Online (Sandbox Code Playgroud)

修改你的 gradle 文件。以这里为例:https : //github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/build.gradle

apply plugin: 'kotlin-allopen'

allOpen {
    // allows mocking for classes w/o directly opening them for release builds
    annotation 'com.android.example.github.testing.OpenClass'
}
Run Code Online (Sandbox Code Playgroud)

现在您可以注释任何类以使其开放进行测试:

@OpenForTesting
class RepoRepository 
Run Code Online (Sandbox Code Playgroud)


and*_*esp 5

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


xue*_*eng 5

实际上有一种方法,我用它来进行间谍活动。只有满足两个先决条件,它才会对你有用:

  1. 您使用某种 DI 注入最终类的实例
  2. 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(); }
}
Run Code Online (Sandbox Code Playgroud)

现在你不仅可以模拟你的最后一堂课,还可以监视它:

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();
Run Code Online (Sandbox Code Playgroud)