Jenkins provides excellent support for integration tests by starting up a complete instance of Jenkins if your test class extends
HudsonTestCase. However, sometimes you might not want to do this - e.g. because you just want to test a single method or just because launching a HudsonTestCase takes a long time (several seconds).
Mocking with Mockito
Unfortunately, it's not easy to test most Jenkins core classes in isolation as they implicitely depend on the
Jenkins#getInstance() singleton, so creating/accessing instances of them often results in
NullPointerExceptions. One technique which comes in handy in this case is stubbing / mocking. There are several good mocking frameworks for Java, but we propose Mockito - it's already included in Jenkins' core dependencies. A basic example looks like this:
This creates a new instance of AbstractBuild which returns FAILURE as build result. All methods which are not explicitly instrumented via one of the Mockito when, doCallRealMethod, doThrow (and so on) methods will do nothing (and return null if they have a return value). Note that there are several other options on how to verify that certain methods are called in your test case or to spy on real objects. You should read the Mockito documentation to get familiar with these features.
More complete examples can be found in existing Jenkins unit test. See for example: SurefireArchiverUnitTest
Handle more complicated cases with PowerMock
In a more complicated case this may still not be enough to test a Jenkins class in isolation. Possible reasons are static or final methods/classes (Mockito can't mock these) or access to Jenkins.getInstance() in constructors. In that case you can use PowerMock/PowerMockito to work around these problems - i.e. removing the 'final' restriction of Mockito or suppressing constructors. See MavenModuleTest for an example how to use it.
Here's an example using PowerMock to mock the
Jenkins#getInstance() singleton from the Github OAuth Plugin.
In the above example, the
GithubLogoutAction class is being tested. It calls
Jenkins.getInstance().getSecurityRealm() (which returns the mocked
PowerMocking the static
Jenkins class appears to negatively interfere with
org.jvnet.hudson.test.HudsonTestCase. When using
HudsonTestCase in the same code above an unhelpful exception is thrown:
java.lang.ClassCastException: com.sun.crypto.provider.AESCipher cannot be cast to javax.crypto.CipherSpi
In case you see CCE happening with similar messages, you may want to prevent the mocking library of changing things in the crypto land.
You can disable that in Powermockito with the following annotation: PowerMockIgnore. Here's one example.
PermGen errors with Java 7 when using PowerMockito
When running your plug-in tests with Java 7 and PowerMockito, you may see PermGen errors. You can increase the PermGen size if you have access to the JVM settings, but sometimes you are not able to change that. In this case, sometimes this error happens when PowerMockito creates several objects in the PermGen, that are never collected. This can be avoided if you add the PrepareForTest annotation to your test.
The class above was not annotated with the PrepareFortest, but had the PowerMockIgnore and RunWith, and there were PermGen errors. After adding it, PowerMockito takes care to carefully create instances as required for that class, keeping a pool and avoiding pollution in the PermGen.