How to mock a final class with mockito

后端 未结 25 1128
日久生厌
日久生厌 2020-11-22 16:35

I have a final class, something like this:

public final class RainOnTrees{

   public void startRain(){

        // some code here
   }
}

I

相关标签:
25条回答
  • 2020-11-22 16:57

    I think you need think more in principle. Instead you final class use his interface and mock interface instead.

    For this:

     public class RainOnTrees{
    
       fun startRain():Observable<Boolean>{
    
            // some code here
       }
    }
    

    add

    interface iRainOnTrees{
      public void startRain():Observable<Boolean>
    }
    

    and mock you interface:

     @Before
        fun setUp() {
            rainService= Mockito.mock(iRainOnTrees::class.java)
    
            `when`(rainService.startRain()).thenReturn(
                just(true).delay(3, TimeUnit.SECONDS)
            )
    
        }
    
    0 讨论(0)
  • 2020-11-22 16:57

    Please look at JMockit. It has extensive documentation with a lot of examples. Here you have an example solution of your problem (to simplify I've added constructor to Seasons to inject mocked RainOnTrees instance):

    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();
            }
    
        }
    }
    
    0 讨论(0)
  • 2020-11-22 16:58

    Mockito 2 now supports final classes and methods!

    But for now that's an "incubating" feature. It requires some steps to activate it which are described in What's New in Mockito 2:

    Mocking of final classes and methods is an incubating, opt-in feature. It uses a combination of Java agent instrumentation and subclassing in order to enable mockability of these types. As this works differently to our current mechanism and this one has different limitations and as we want to gather experience and user feedback, this feature had to be explicitly activated to be available ; it can be done via the mockito extension mechanism by creating the file src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker containing a single line:

    mock-maker-inline
    

    After you created this file, Mockito will automatically use this new engine and one can do :

     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());
    

    In subsequent milestones, the team will bring a programmatic way of using this feature. We will identify and provide support for all unmockable scenarios. Stay tuned and please let us know what you think of this feature!

    0 讨论(0)
  • 2020-11-22 17:00

    You cannot mock a final class with Mockito, as you can't do it by yourself.

    What I do, is to create a non-final class to wrap the final class and use as delegate. An example of this is TwitterFactory class, and this is my mockable class:

    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();
        }
    }
    

    The disadvantage is that there is a lot of boilerplate code; the advantage is that you can add some methods that may relate to your application business (like the getInstance that is taking a user instead of an accessToken, in the above case).

    In your case I would create a non-final RainOnTrees class that delegate to the final class. Or, if you can make it non-final, it would be better.

    0 讨论(0)
  • 2020-11-22 17:00

    Didn't try final, but for private, using reflection remove the modifier worked ! have checked further, it doesn't work for final.

    0 讨论(0)
  • 2020-11-22 17:01

    Another workaround, which may apply in some cases, is to create an interface that is implemented by that final class, change the code to use the interface instead of the concrete class and then mock the interface. This lets you separate the contract (interface) from the implementation (final class). Of course, if what you want is really to bind to the final class, this will not apply.

    0 讨论(0)
提交回复
热议问题