Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何用mockito模拟最后一个类_Java_Junit_Mockito - Fatal编程技术网

Java 如何用mockito模拟最后一个类

Java 如何用mockito模拟最后一个类,java,junit,mockito,Java,Junit,Mockito,我有最后一节课,类似这样: public final class RainOnTrees{ public void startRain(){ // some code here } } public class Seasons{ RainOnTrees rain = new RainOnTrees(); public void findSeasonAndRain(){ rain.startRain(); } } 我在

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

public final class RainOnTrees{

   public void startRain(){

        // some code here
   }
}
public class Seasons{

   RainOnTrees rain = new RainOnTrees();

   public void findSeasonAndRain(){

        rain.startRain();

    }
}
我在其他类似的课程中使用此课程:

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测试类中,我想模拟
RainContrees
类。我怎样才能用Mockito做到这一点?

将此项添加到gradle文件中:

testImplementation 'org.mockito:mockito-inline:2.13.0'
testCompile group: 'org.mockito', name: 'mockito-inline', version: '2.8.9'
这在Mockito v1中是不可能的,从:

Mockito的局限性是什么

  • 需要Java1.5+

  • 无法模拟最终类


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

我要做的是创建一个非final类来包装final类并用作委托。这方面的一个例子是类,这是我的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)


在您的情况下,我将创建一个非final
raInContrees
类,委托给final类。或者,如果您可以将其设置为非最终版本,则更好。

使用Powermock。此链接显示如何执行此操作:

我遇到了相同的问题。因为我试图模拟的类是一个简单的类,所以我只是创建了它的一个实例并返回了它。

是的,这里有同样的问题,我们不能用Mockito模拟最后一个类。准确地说,Mockito无法模拟/刺探以下内容:

  • 期末班
  • 匿名类
  • 基本类型
但是在我看来,使用包装器类是一个巨大的代价,所以改用PowerMockito。它有大量的文档和大量的例子。这里有一个问题的示例解决方案(为了简化,我在
Seasons
中添加了构造函数,以注入模拟的
RainContrees
实例):

尝试一下:

Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable));
这对我有用。“SomeMockableType.class”是您要模拟或监视的对象的父类,而SomeInstanceThatNotMockableorSpyable是您要模拟或监视的实际类


有关更多详细信息,请查看另一种解决方法,在某些情况下可能适用,即创建由最终类实现的接口,更改代码以使用接口而不是具体类,然后模拟接口。这允许您将契约(接口)与实现(最终类)分开。当然,如果您真正想要绑定到最后一个类,这将不适用。

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

尽管Mockito不能,但根据设计,模拟最终类,委托方法是可能的。这有其优点:

  • 如果API一开始就打算将类更改为非final(final类有自己的特性),那么您不会被迫将类更改为非final
  • 您正在测试围绕您的API进行测试的可能性
  • 在测试用例中,您故意将调用转发到被测试的系统。因此,根据设计,您的装饰没有任何作用

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

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

    没有尝试final,但对于private,使用反射移除修改器是有效的!进一步检查后,它对final不起作用。

    我猜您之所以选择了
    final
    ,是因为您想阻止其他类扩展
    RainOnTrees
    。正如所建议的(第15项),还有另一种方法可以使类关闭以进行扩展,而不必使其成为最终类:

  • 删除
    final
    关键字

  • 使其构造函数
    私有
    。没有类能够扩展它,因为它不能调用
    super
    构造函数

  • 创建一个静态工厂方法来实例化类

    // 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
        }
    }
    

  • 通过使用此策略,您将能够使用Mockito并使用少量样板代码关闭您的类进行扩展。

    实际上有一种方法,我用于监视。只有满足以下两个先决条件,它才会对您有效:

  • 您使用某种DI注入最终类的实例
  • 最后一个类实现一个接口
  • 请回顾第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();
    

    Mockito 2现在支持final类和方法

    但就目前而言,这是一个“孵化”功能。激活它需要一些步骤,如中所述:

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

    mock-maker-inline
    
    创建此文件后,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());
    
    在随后的里程碑中,团队将提供使用此功能的编程方式。我们将确定并支持所有不可修改的场景。请继续关注,并让我们知道您对该功能的看法


    正如其他人所说的那样,Mockito无法做到这一点。我建议使用反射来设置特定的f
    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));
    
    }
    
    testCompile group: 'org.mockito', name: 'mockito-inline', version: '2.8.9'
    
    testImplementation 'org.mockito:mockito-inline:2.13.0'
    
    testImplementation 'net.bytebuddy:byte-buddy-agent:1.10.19'
    
    /**
     * 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
    
    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 
    
     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)
            )
    
        }
    
    mock-maker-inline
    
    androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.1"