(Original keynote slides can be found at https://github.com/Endran/PublicSlides)
For years the industry standard of mocking on the JVM has been Mockito. Mockito is a wonderful library that really speeds up your testing by allowing you to create mocks in a very simple way. That being said, it does have its drawbacks, for which different strategies need to be deployed to keep your code testable. The main drawbacks are statics and finals. Final classes cannot be mocked, nor final methods, and also static methods are a no-go. To work with these type of things we need to wrap it, and copy the signature in a non final, non static way.
I have a great adversity against statics, I've devoted an entire post about it, in short; It hides dependencies and brings so little convenience at the costs of its drawbacks. Finals on the other hand have purpose, it helps messaging the goal of a class or method. Java is one of the few languages where classes and methods are open/virtual by default and have to be closed/final by explicit action. In (for example) Kotlin, everything is final by default, if you do not want something to be final, you should use the open keyword.
No matter if you follow the principle of making things final, static or not, if you are using Mockito the decision has been made. This mocking framework demands that everything is non-final, demands that everything is designed to be extended, since it might need to be mocked away. We should be able to improve upon this, and by the name of this post, you should be able to guess which framework will save the day. JMockit will help us with our impediments, and will give some other nifty benefits as well!
3. In all it’s glory
• Has been around since 2008
• Is the industry standard ever since
• In 2013 more then 30.000 Github project
• More popular then Spring and Guava
• Most distinguishing feature
• Verify the behaviour of the SUT without
establishing expectations beforehand
3
7. So for, so good
• Arrange, Act, Assert
• Mock any class, interface, method
• No need for introducing test interface
shims
• Declarative style for setting up behaviour
• Declarative style for verifying behaviour
• …
• In the happy flow
7
9. This gonna be good
• Mocking void methods
• Mocking finals
• Mocking statics
• Verifying constructors
9
10. This gonna be good
• Make a test shim
• Extend the class and scoop the methods
• Make an adapter or facade
• Why aren’t they using interfaces in the
first place
• The final keyword should be banned
• Let’s agree not to test this
• Ahh.. let’s use
PowerMockito!
10
18. Annotations - With great power…
18
• @Injectable
• Single mock instance
• @Mocked
• Entire class mock (at bytecode level)
• @Capturing
• Any derived instance and subclass will be a class mock
• @Tested
• System under test
• Tries to satisfy Constructor, @Autowired, @Injectable
21. Real world examples
21
java.lang.RuntimeException: Method getWindow in android.app.Activity not mocked. See
http://g.co/androidstudio/not-mocked for details.
at android.app.Activity.getWindow(Activity.java)
at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:117)
at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:456)
at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:59)
at ExampleActivity.onCreate(ExampleActivity.java:15)
at ExampleActivityTest.
shouldCommitFragment_whenOnCreateIsCalled(ExampleActivityTest.java:9)
22. Real world examples
22
akka.actor.ActorInitializationException: You cannot create an instance of
[com.cc.LoggingHandler] explicitly using the constructor (new). You have to use one of
the 'actorOf' factory methods to create a new actor. See the documentation.
at akka.actor.ActorInitializationException$.apply(Actor.scala:173)
at akka.actor.Actor$class.$init$(Actor.scala:436)
at akka.actor.UntypedActor.<init>(UntypedActor.scala:97)
at com.cc.LoggingHandler.<init>(LoggingHandler.java:14)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at …
26. JMockit’s Advantages
• Mockito’s distinguishing feature covered by
JMockit
• Verify the behaviour of the SUT without establishing expectations
beforehand
• No need for special test runners
• No limit on testing statics, finals,
constructors and whatnot
• No limitations/anomalies in your code due
to the test framework
26
27. JMockit’s Disadvantages
• Risk of introducing in your code
• Static methods
• Not using dependency injection
• Need to get accustomed to new mocking
framework
• Mockito’s error messages are more
explicit
• Mockito forces proper dependencies
27
28. Some figures
• Github grade and maven dependencies
• Mockito: 131.525
• JMockit: 3.058
• First mentioned - first release:
• Mockito: 2008 - 2008
• JMockit: 2006 - 2012
• Both under constant development
28
29. Read more
• Official pages:
• Mockito: http://mockito.org/
• JMockit: http://jmockit.org/
• My blog:
• www.endran.nl/blog
• Mockito vs JMockit, battle of the
mocking frameworks
• Death to statics
29
Final class wont work
Final method wont work
Static method cannot be mocked
Thread.sleep will be executed every time
Hint about the annotaions, details in next slides
Expectations is like Mockito when, not necessary for verifications
Times 1 is optional
Thread sleep can just be mocked
minTimes is optional. Set to 0 to be exactly like Mockito when.
Expectations need to be met. Not meeting expectations will fail your test.
GoogleMock users can still do their expectations beforehand, Mockito user can still do verification afterwards.
Any mated with matching signature
A captor is just a list with the type you want to capture.
Assertions can be done in the Verifications, or outside of them. Dealers choice.
This legacy code doesn’t have Dependency injection
Just mock the constructor
Akka provides a mechanism to create test actors and whatnot, but it also does some lifecycle methods.
This unit test only tests our code, and ignores framework limitations
Akka provides a mechanism to create test actors and whatnot, but it also does some lifecycle methods.
This unit test only tests our code, and ignores framework limitations
This is the same test as the @Capture before. Without @Capture we need to setup the entire test suite ourselves
Before setup the mocks are created, after setup @Tested is created. Static constructor dependencies are resolved after the setup, before the test.
Since we do not know exactly which concrete class Logger is, @Captor will mock any Logger.