Mockito 2 and junit 5 : mocking classes - mockito

I have a strange behaviour with mockito 2 and Junit 5 : Mockito can't mock a class.
I extracted my test to a simple test case :
#ExtendWith(MockitoJavaExtension.class)
class JavaTest {
#Test
void shouldMockClass(){
final MockedJavaClass mock = mock(MockedJavaClass.class);
when(mock.execute()).thenReturn(Collections.singletonList("some value"));
assertEquals(1, mock.execute().size());
}
#Test
void shouldMockInterface(){
final MockedJavaInterface mock = mock(MockedJavaInterface.class);
when(mock.execute()).thenReturn(Collections.singletonList("some value"));
assertEquals(1, mock.execute().size());
}
}
class MockedJavaClass{
List<String> execute(){
throw new IllegalArgumentException();
}
}
interface MockedJavaInterface{
default List<String> execute(){
throw new IllegalArgumentException();
}
}
When I run this test, I get the IllegalArgumentException :
JUnit Jupiter:JavaTest:shouldMockClass()
MethodSource [className = 'JavaTest', methodName = 'shouldMockClass', methodParameterTypes = '']
=> java.lang.IllegalArgumentException
MockedJavaClass.execute(JavaTest.java:36)
JavaTest.shouldMockClass(JavaTest.java:19)
It means that the class is not mocked at all.
I also tried with an external class (not an inner one), and the issue is still the same...
This is also my build.gradle :
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.junit.platform:junit-platform-gradle-plugin:1.1.1"
}
}
apply plugin: 'idea'
apply plugin: 'java'
apply plugin: 'org.junit.platform.gradle.plugin'
sourceCompatibility = 8
targetCompatibility = 8
repositories {
mavenCentral()
}
dependencies {
testCompile "org.mockito:mockito-core:2.18.0"
testCompile "org.mockito:mockito-junit-jupiter:2.18.0"
testCompile("org.junit.jupiter:junit-jupiter-api:5.1.1")
testCompile("org.junit.jupiter:junit-jupiter-params:5.1.1")
testRuntime("org.junit.jupiter:junit-jupiter-engine:5.1.1")
testRuntime("org.junit.platform:junit-platform-launcher:1.1.1")
}
junitPlatform {
filters {
engines {
include 'junit-jupiter'
}
includeClassNamePattern '.*Test'
}
}
I also tried with the mockito junit5 extension to inject mocks, but I still have the problem...
Any help will be appreciated !

I finally ended finding the solution : My first test was a Kotlin one, in a "open class" but a non-opened method declaration.
Mockito can't mock a method with a package access nor a final method !
In this particular case, it just calls the real method when you try to mock it, without telling you anything...
Maybe this post will be useful for someone ! –

You didn't mock the execute function on your MockClass so it used the implementation of execute that was in the class definition - in other words the IllegalArgumentException.
You need to setup your mocks such that they provide dummy implementations of the methods that are going to be required as dependancies to your test case. In this particular case it doesn't appear you are testing anything really, just learning Mockito?
So yea, the answer is - provide mock implementations or the default will be used.

Related

How to use Dagger for Groovy?

How to configure dagger to inject groovy classes, and to inject into groovy classes?
I was initially trying to get dagger to inject a groovy class into my java app, and I found dagger was complaining the groovy class is not found. Looking at the log, it seems that compileGroovy happens after compileJava. And the annotation processing of dagger compiler seems to be in compileJava. I guessed that might be the problem -- no groovy classes are available at this time. But I've yet figured out a way to coerce either of dagger or groovy to work with the other.
It seems I could not upload a .tar.gz. But if anyone needs a minimal demo code for what I meant to achieve, these might help (with gradle 7):
build.gradle:
plugins {
id 'groovy'
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
annotationProcessor 'com.google.dagger:dagger-compiler:2.+'
implementation 'com.google.dagger:dagger:2.+'
implementation 'org.codehaus.groovy:groovy-all:3.+'
}
settings.gradle:
rootProject.name = 'groovy-dagger1'
src/main/groovy/org/example/dagger/MainComponent.groovy:
package org.example.dagger
import dagger.Component
#Component(modules = [
MainModule,
])
interface MainComponent {
String message();
}
src/main/groovy/org/example/dagger/MainModule.groovy:
package org.example.dagger
import dagger.Module
import dagger.Provides
#Module
final class MainModule {
#Provides
static String message() {
return 'Hello Groovy Dagger!'
}
}
src/main/groovy/org/example/main/Main.groovy:
package org.example.main;
class Main {
static void main(String[] args) {
// Dagger component does not exist :/
// println DaggerMainComponent.create().message()
}
}
By default, the groovy compiler will not run the java annotation processors...
You can add this to your build.gradle:
compileGroovy {
groovyOptions.javaAnnotationProcessing = true
}
You will of course need to add an import
import org.example.dagger.DaggerMainComponent
To Main.groovy

How can I run code in JUnit before Spring starts?

How can I run code in my #RunWith(SpringRunner.class) #SpringBootTest(classes = {...}) JUnit test before Spring starts?
This question has been asked several times (e.g. 1, 2) but was always "solved" by some configuration recommendation or other, never with a universal answer. Kindly don't question what I am about to do in that code but simply suggest a clean way to do it.
Tried so far and failed:
Extend SpringJUnit4ClassRunner to get a class whose constructor can run custom code before initializing Spring. Failed because super(testClass) must be called first thing and already does a whole lot of things that get in the way.
Extend Runner to get a class that delegates to SpringRunner instead of inheriting it. This class could run custom code in its constructor before actually instantiating the SpringRunner. However, this setup fails with obscure error messages like java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig. "Obscure" because my test has no web config and thus shouldn't meddle with sessions and cookies.
Adding an ApplicationContextInitializer that is triggered before Spring loads its context. These things are easy to add to the actual #SpringApplication, but hard to add in Junit. They are also quite late in the process, and a lot of Spring has already started.
One way to do it is to leave out SpringRunner and use the equivalent combination of SpringClassRule and SpringMethodRule instead. Then you can wrap the SpringClassRule and do your stuff before it kicks in:
public class SomeSpringTest {
#ClassRule
public static final TestRule TestRule = new TestRule() {
private final SpringClassRule springClassRule =
new SpringClassRule();
#Override
public Statement apply(Statement statement, Description description) {
System.out.println("Before everything Spring does");
return springClassRule.apply(statement, description);
}
};
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Test
public void test() {
// ...
}
}
(Tested with 5.1.4.RELEASE Spring verison)
I don't think you can get more "before" than that. As for other options you could also check out #BootstrapWith and #TestExecutionListeners annotations.
Complementing jannis' comment on the question, the option to create an alternative JUnit runner and let it delegate to the SpringRunner does work:
public class AlternativeSpringRunner extends Runner {
private SpringRunner springRunner;
public AlternativeSpringRunner(Class testClass) {
doSomethingBeforeSpringStarts();
springRunner = new SpringRunner(testClass);
}
private doSomethingBeforeSpringStarts() {
// whatever
}
public Description getDescription() {
return springRunner.getDescription();
}
public void run(RunNotifier notifier) {
springRunner.run(notifier);
}
}
Being based on spring-test 4.3.9.RELEASE, I had to override spring-core and spring-tx, plus javax.servlet's servlet-api with higher versions to make this work.

Running Groovy test cases with JUnit 5

Maybe this is very simple, but I couldn't find any examples on the web:
I'd like to use JUnit 5 to run a unit test implemented as a Groovy class. My current setup seems to launch JUnit 5, but fail to detect the test case. IntelliJ recognizes the test, but fails to run it. If I add a Java unit test, it is launched correctly.
Here's what I have now:
Project structure
src
main
groovy
# production code
test
groovy
UnitTest.groovy
build.gradle
...
build.gradle
plugins {
id 'groovy'
}
dependencies {
compile localGroovy()
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.1'
}
test {
useJUnitPlatform()
}
UnitTest.groovy
import org.junit.jupiter.api.Test
class UnitTest {
#Test
def shouldDoStuff() {
throw new RuntimeException()
}
}
I'm using Gradle 4.10.
Any ideas?
JUnit requires all testing method to use return type void. Groovy's def keyword is compiled to an Object type, so your method compiles to something like this in Java:
import org.junit.jupiter.api.Test
public class UnitTest {
#Test
Object shouldDoStuff() {
throw new RuntimeException();
}
}
If you try this out as a Java test, it won't find the test case neither. The solution is very simple - replace def with void and your Groovy
test case will be executed correctly.
src/test/groovy/UnitTest.groovy
import org.junit.jupiter.api.Test
class UnitTest {
#Test
void shouldDoStuff() {
throw new RuntimeException()
}
}
Demo:

Parameter specified as non-null is null when using Mokito anyObject() on Kotlin function

My code as below, refering to the solution in https://stackoverflow.com/a/30308199/3286489
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.*
class SimpleClassTest {
private fun <T> anyObject(): T {
Mockito.anyObject<T>()
return uninitialized()
}
private fun <T> uninitialized(): T = null as T
lateinit var simpleObject: SimpleClass
#Mock lateinit var injectedObject: InjectedClass
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
}
#Test
fun testSimpleFunction() {
simpleObject = SimpleClass(injectedObject)
verify(injectedObject).settingDependentObject(anyObject())
}
}
I still have the below error
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method my.package.InjectedClass.settingDependentObject, parameter dependentObject
Did I miss anything?
UPDATED
Below is the code tested (simplest form and working)
class SimpleClass(val injectedClass: InjectedClass) {
fun simpleFunction() {
injectedClass.settingDependentObject(DependentClass(Response.Builder().build()))
}
}
open class DependentClass(response: Response) {
}
open class InjectedClass() {
lateinit var dependentObject: DependentClass
fun settingDependentObject(dependentObject: DependentClass) {
this.dependentObject = dependentObject
}
}
By default Kotlin classes and members are final. Mockito cannot mock final classes or methods.
Thus when you write:
verify(injectedObject).settingDependentObject(anyObject())
the real implementation is called which requires non null argument.
To fix that either open your class and method or, even better, change SimpleClass to accept an interface as its constructor argument and mock the interface instead.
There is a project specifically to help deal with Kotlin "closed by default" in unit testing with Mockito. For JUNIT, you can use the kotlin-testrunner which is an easy way to make any Kotlin test automatically open up classes for testing as they are loaded by the classloader. Usage is simple, just add one annotation of #RunWith(KotlinTestRunner::class), for example:
#RunWith(KotlinTestRunner::class)
class MyKotlinTestclass {
#Test
fun test() {
...
}
}
This is thoroughly covered in the article Never say final: mocking Kotlin classes in unit tests
This covers your use case in an automatic way by allowing all classes to be mocked that otherwise would not be allowed.
I ran into the same issue with Mockito when using RETURNS_DEEP_STUBS. It seems like nulls are still returned for nested objects, even when using the kotlin-allopen plugin.
Please check out and comment on this issue on Mockito if you're having the same problem.
You can use this function instead
inline fun <reified T : Any> any(): T = Mockito.any(T::class.java) ?: T::class.java.newInstance()

JMockit MockUp persisting between Spock tests

I'm using a Spock test written in Groovy to test some Java code. I'm using JMockit to mock methods in the java code, as Spock only handles mocking Groovy classes. I'm running into a problem where a JMockit MockUp is persisting between tests. Such a mock instance should only exist for the test (per JMockit documentation), but this isn't working, and I imagine it's because it's not using the JMockit test runner, and rather the Spock test runner.
Here is the simplest example of the problem I'm facing. I have a simple method returning a string, I can change the return value of the method with MockUp but it still exists for the third test, which doesn't expect it to be used.
Java Class
public class ClassToTest {
public String method() {
return "original";
}
}
Spock Test
class ClassToTestSpec extends Specification {
void "first test"() {
when:
String result = new ClassToTest().method()
then:
result == "original"
}
void "second test"() {
setup:
new MockUp<ClassToTest>() {
#Mock
public String method() {
return "mocked"
}
}
when:
String result = new ClassToTest().method()
then:
result == "mocked"
}
void "third test"() {
when:
String result = new ClassToTest().method()
then:
result == "original"
}
}
The third test fails, because ClassToTest.method() still returns the String "mocked" rather than "original". Using a debugger I have validated that the Mocked method is called twice.
Question
Is there any way to manually remove a class MockUp in JMockit? Thanks.
You can call the MockUp.tearDown method on the created mockup object, to manually undo its effects.
Not exactly an answer to the question - because I still don't know if JMockit's MockUp can be manually removed. But thanks to #PeterNiederwieser's comments, I found that you can actually create a partial mock for a Java class. Below is the change to the second test from above.
void "second test"() {
setup:
ClassToTest test = Spy(ClassToTest) {
method() >> "mocked"
}
when:
String result = test.method()
then:
result == "mocked"
}
Peter mentioned reconsidering how and what to test if a Spy() is necessary, but for my use case this is preferred.

Resources