我有最后一堂课,是这样的:
public final class RainOnTrees{
public void startRain(){
// some code here
}
}
我在像这样的其他类中使用这个类:
public class Seasons{
RainOnTrees rain = new RainOnTrees();
public void findSeasonAndRain(){
rain.startRain();
}
}
在我的 Seasons.java
的 JUnit 测试类中,我想模拟 RainOnTrees
类。我怎么能用 Mockito 做到这一点?
Mockito 2 现在支持 final 类和方法!
但就目前而言,这是一个“孵化”功能。它需要一些步骤来激活它,如 What's New in Mockito 2 中所述:
最终类和方法的模拟是一个孵化、选择加入的特性。它结合使用 Java 代理工具和子类化,以实现这些类型的可模拟性。由于这与我们当前的机制不同,并且这个机制有不同的限制,并且我们想要收集经验和用户反馈,因此必须明确激活此功能才能使用;它可以通过 mockito 扩展机制通过创建包含一行的文件 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker 来完成:mock-maker-inline 创建此文件后,Mockito 将自动使用这个新引擎可以做到:final class FinalClass { final String finalMethod() { return "something"; } } FinalClass 具体 = new FinalClass(); FinalClass 模拟 = 模拟(FinalClass.class); given(mock.finalMethod()).willReturn("不再"); assertThat(mock.finalMethod()).isNotEqualTo(concrete.finalMethod());在随后的里程碑中,团队将带来使用此功能的程序化方式。我们将为所有不可模拟的场景识别并提供支持。请继续关注,请让我们知道您对此功能的看法!
Mocking final/static classes/methods is possible with Mockito v2 only.
将此添加到您的 gradle 文件中:
testImplementation 'org.mockito:mockito-inline:2.13.0'
这对于来自 Mockito FAQ 的 Mockito v1 是不可能的:
Mockito Needs java 1.5+ 有什么限制不能模拟最终类...
mockito-core
“替换”,而不是“添加”,mockito-inline
。 javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/…
mockito-inline
的这种额外依赖。只需要答案 stackoverflow.com/a/40018295/1417088 中的“org.mockito.plugins.MockMaker”文件。
在你的构建文件中添加这个:
如果使用 gradle:build.gradle
testImplementation 'org.mockito:mockito-inline:2.13.0'
如果使用 maven:pom.xml
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>2.13.0</version>
<scope>test</scope>
</dependency>
这是使 mockito 与 final 类一起工作的配置
如果您遇到 Could not initialize inline Byte Buddy mock maker. (This mock maker is not supported on Android.)
将 Byte Buddy 依赖项添加到您的 build.gradle
文件:
testImplementation 'net.bytebuddy:byte-buddy-agent:1.10.19'
来源:https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy
org.mockito.exceptions.base.MockitoInitializationException: Could not initialize inline Byte Buddy mock maker. (This mock maker is not supported on Android.)
你不能用 Mockito 模拟最后一堂课,因为你不能自己做。
我所做的是创建一个非最终类来包装最终类并用作委托。 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();
}
}
缺点是样板代码很多;优点是您可以添加一些可能与您的应用程序业务相关的方法(例如在上述情况下获取用户而不是 accessToken 的 getInstance)。
在您的情况下,我将创建一个委托给最终类的非最终 RainOnTrees
类。或者,如果你可以使它成为非最终版本,那就更好了。
@Delegate
之类的东西来处理大量样板文件。
在 Mockito 3 及更多版本中,我遇到了同样的问题并从这个链接修复了它
Mock Final Classes and Methods with Mockito 如下
在 Mockito 可以用于模拟 final 类和方法之前,需要 > 配置它。我们需要在项目的 src/test/resources/mockito-extensions 目录中添加一个名为 org.mockito.plugins.MockMaker 的文本文件,并添加一行文本:mock-maker-inline Mockito 检查扩展目录中的配置文件时它已加载。此文件启用最终方法和类的模拟。
使用 Powermock。此链接显示如何操作:https://github.com/jayway/powermock/wiki/MockFinal
Powermock
来模拟最终类和静态方法,以增加在 Sonarqube
上正式检查的覆盖率。自 SonarQube 以来,覆盖率为 0%,无论出于何种原因,它都无法识别在其中任何地方使用 Powermock 的类。我和我的团队花了相当长的时间从网上的某个帖子中意识到这一点。所以这只是小心使用 Powermock 并且可能不使用它的原因之一。
只是为了跟进。请将此行添加到您的 gradle 文件中:
testCompile group: 'org.mockito', name: 'mockito-inline', version: '2.8.9'
我尝试过各种版本的 mockito-core 和 mockito-all。它们都不起作用。
我猜您之所以这样做是final
,因为您想阻止其他类扩展 RainOnTrees
。正如 Effective Java 所建议的(第 15 项),还有另一种方法可以使类关闭以进行扩展,而不使其成为 final
:
去掉final关键字;将其构造函数设为私有。没有类能够扩展它,因为它不能调用超级构造函数;创建一个静态工厂方法来实例化您的类。 // 这里不再有 final 关键字。公共类 RainOnTrees { public static RainOnTrees newInstance() { return new RainOnTrees(); } private RainOnTrees() { // 私有构造函数。 } public void startRain() { // 这里有一些代码 } }
通过使用此策略,您将能够使用 Mockito 并保持您的类关闭,以便使用少量样板代码进行扩展。
我有同样的问题。由于我试图模拟的类是一个简单的类,我只是创建了它的一个实例并返回它。
在某些情况下可能适用的另一种解决方法是创建一个由该最终类实现的接口,更改代码以使用该接口而不是具体类,然后模拟该接口。这使您可以将合同(接口)与实现(最终类)分开。当然,如果你真正想要的是绑定到最终类,这将不适用。
为在 Android + Kotlin 上面临相同问题(Mockito + Final Class)的人节省时间。在 Kotlin 中,类默认是 final 的。我在一个带有 Architecture 组件的 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
修改你的 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'
}
现在您可以注释任何类以使其开放以供测试:
@OpenForTesting
class RepoRepository
试试这个:
Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable));
它对我有用。 “SomeMockableType.class”是您要模拟或监视的父类,而 someInstanceThatIsNotMockableOrSpyable 是您要模拟或监视的实际类。
有关更多详细信息,请查看here
实际上有一种方法,我用它来进行间谍活动。只有满足两个先决条件,它才会为您工作:
您使用某种 DI 注入 final 类的实例 Final 类实现了一个接口
请回忆 Effective Java 中的第 16 项。您可以创建一个包装器(不是最终的)并将所有调用转发到最终类的实例:
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.MockMaker”的简单文件,并将其放在名为“mockito-extensions”的文件夹中。这个文件夹应该在类路径中可用。 2. 上面创建的文件的内容应该是一行,如下所示:mock-maker-inline
上述两个步骤是激活 mockito 扩展机制并使用此选择加入功能所必需的。
示例类如下:-
最终类.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));
}
}
希望能帮助到你。
此处提供完整的文章mocking-the-unmockable。
是的,同样的问题,我们不能用 Mockito 模拟最终课程。准确地说,Mockito 不能模拟/监视以下内容:
最后的课程
匿名类
原始类型
但是在我看来,使用包装类似乎要付出很大的代价,所以请改用 PowerMockito。
我认为您需要在原则上进行更多思考。相反,您最终的课程使用他的接口和模拟接口。
为了这:
public class RainOnTrees{
fun startRain():Observable<Boolean>{
// some code here
}
}
添加
interface iRainOnTrees{
public void startRain():Observable<Boolean>
}
并模拟你的界面:
@Before
fun setUp() {
rainService= Mockito.mock(iRainOnTrees::class.java)
`when`(rainService.startRain()).thenReturn(
just(true).delay(3, TimeUnit.SECONDS)
)
}
请查看JMockit。它有大量文档和大量示例。在这里,您有一个解决问题的示例(为了简化,我已将构造函数添加到 Seasons
以注入模拟的 RainOnTrees
实例):
package jmockitexample;
import mockit.Mocked;
import mockit.Verifications;
import mockit.integration.junit4.JMockit;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(JMockit.class)
public class SeasonsTest {
@Test
public void shouldStartRain(@Mocked final RainOnTrees rain) {
Seasons seasons = new Seasons(rain);
seasons.findSeasonAndRain();
new Verifications() {{
rain.startRain();
}};
}
public final class RainOnTrees {
public void startRain() {
// some code here
}
}
public class Seasons {
private final RainOnTrees rain;
public Seasons(RainOnTrees rain) {
this.rain = rain;
}
public void findSeasonAndRain() {
rain.startRain();
}
}
}
RC 和 Luigi R. Viggiano 共同提供的解决方案可能是最好的主意。
尽管 Mockito 在设计上不能模拟最终类,但委托方法是可能的。这有它的优点:
如果这是您的 API 最初的意图,您不必将您的类更改为非最终类(最终类有其好处)。您正在测试围绕您的 API 进行装饰的可能性。
在您的测试用例中,您故意将调用转发到被测系统。因此,按照设计,您的装饰不会做任何事情。
因此,您的测试还可以证明用户只能装饰 API 而不能扩展它。
在更主观的方面:我更喜欢将框架保持在最低限度,这就是为什么 JUnit 和 Mockito 通常对我来说就足够了。事实上,限制这种方式有时也会迫使我永远重构。
如果您尝试在 test 文件夹下运行单元测试,则最佳解决方案很好。只需按照它添加扩展名即可。
但是,如果您想使用 androidtest 文件夹下的 android 相关 类(如上下文或活动)运行它,the answer 适合您。
添加这些依赖项以成功运行 mockito:
testImplementation 'org.mockito:mockito-core:2.24.5' testImplementation "org.mockito:mockito-inline:2.24.5"
根据这个 GitHub 问题,mockito-android 不支持模拟最终类。您应该为此使用 Mockk。
对于单元测试和 ui 测试,您都可以毫无问题地使用 Mockk。
如果您需要在 Android 的插桩测试中使用 Mockito(即在 Android 设备上运行),则不能使用 mockito-inline
。有一个特殊的 mockito-android
版本也不能解决“final class”问题。唯一可行的解决方案是 Dexmaker library。唯一的限制是它仅适用于 Android P(Android 9,API 28)及更高版本。可以按如下方式导入:
androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.1"
请注意,还有一个“dexmaker-mockito”版本也不适用于最终课程。确保导入“dexmaker-mockito-inline”。
正如其他人所说,这不适用于 Mockito。我建议使用反射来设置被测代码正在使用的对象上的特定字段。如果您发现自己经常这样做,您可以将此功能包装在一个库中。
顺便说一句,如果您是最后一个打分班,请停止这样做。我遇到了这个问题,因为我正在使用一个 API,其中所有内容都标记为 final 以防止我对扩展(模拟)的合法需求,我希望开发人员没有假设我永远不需要扩展类。
final
应该是默认值。
对我们来说,这是因为我们从 koin-test 中排除了 mockito-inline。一个 gradle 模块实际上需要这个,因此仅在发布版本中失败(IDE 中的调试版本有效):-P
对于最终类,在下面添加模拟并调用静态或非静态。
1-将其添加到类级别 @SuppressStatucInitializationFor(value ={class name with package}) 2- PowerMockito.mockStatic(classname.class) 将模拟类 3-然后在调用此类的方法时使用您的 when 语句返回模拟对象。
享受
我能够克服这个消息:
org.mockito.exceptions.base.MockitoException:无法模拟/监视类 org.slf4j.impl.Log4jLoggerAdapter Mockito 无法模拟/监视,因为:最终或匿名类
由此:log = spy(log);
通过使用它来代替:
log = mock(Logger.class);
然后它工作。
我猜“默认”记录器适配器是最终类的一个实例,所以我不能“窥探”它,但我可以模拟整个事情。去搞清楚...
这可能意味着如果您也有方便的话,您可以将它替换为其他一些“非最终”实例。或简化版本等。FWIW ...
没有尝试最终,但对于私有,使用反射删除修饰符有效!进一步检查,它不适用于final。
org.mockito.plugins.MockMaker
文件放在正确的文件夹中。