Mock final class with Mockito 2 - java

I'm removing Powermock from the project I'm currently working on, so I'm trying to rewrite some existing unitary test only with Mockito (mockito-core-2.2.28).
When I run the test, I have the following error:
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class com.ExternalpackagePath.Externalclass
Mockito cannot mock/spy because :
final class
I know that this question has already been asked (How to mock a final class with mockito, Mock objects calling final classes static methods with Mockito), but I didn't find the answer I'm looking for.
Here is an extract of my code :
public class MyClassToTest extends TestCase {
private MyClass myClass;
#Mock private Externalclass ext; // This class is final, I would like to mock it
#Override
protected void setUp() throws Exception {
MockitoAnnotations.initMocks(this); // <<<< The exception is thrown here
ext = Mockito.mock(Externalclass.class);
}
}
As mentioned in the Mockito documentation (https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2, §Mock the unmockable), I added the org.mockito.plugins.MockMaker file. This is the tree of my project :
project
src
com.packagePath.myPackage
myClass
test
com.packagePath.myPackage
myClassToTest
resources
mockito-extensions
org.mockito.plugins.MockMaker
I also tries to put the "resources" directory in "src", in a subdir called "test", but the result is still the same.
I thought that mocking a final was possible with Mockito v2. Does someone have an idea of what is missing here ?
Thanks!

Weird that your solution seems to work.
According to their documentation on Github it says.
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!
My working structure now looks like this.

I couldn't get it working with the configuration file either; however, the Mockito team is so kind and also provides a pre-configured Mockito artifact that requires no configuration in the target project.
As a convenience, the Mockito team provides an artifact where this mock maker is preconfigured. Instead of using the mockito-core artifact, include the mockito-inline artifact in your project. Note that this artifact is likely to be discontinued once mocking of final classes and methods gets integrated into the default mock maker.
So, if you use Gradle and want to test your Kotlin code, just add this to your project's dependencies:
testCompile 'org.mockito:mockito-inline:2.8.9'
testCompile('com.nhaarman:mockito-kotlin:1.5.0') {
exclude group: 'org.jetbrains.kotlin'
exclude group: 'org.mockito'
}

Well, I found what's wrong here, it maybe useful for other people. My project tree is wrong, I put the org.mockito.plugins.MockMaker in a directory "mockito-extension" directly in "src". This is my tree now:
projet
src
com.packagePath.myPackage
myClass
mockito-extensions
org.mockito.plugins.MockMaker
test
com.packagePath.myPackage
myClassToTest

You seem to have had a classpath issue, just like I did.
Your previous setup would have also worked, but it seems like
project/test/resources
was not in your classpath.
I had the same issue when I tried to run this with IntelliJ. I simply marked the resources directory as a Test Resources Root and it worked fine. Praise the gods of Mockito!

I had the same issue that you described. For me, the solution was to create a file named org.mockito.plugins.MockMaker in /test/java/resources/mockito-extensions/ directory and write the following line: mock-maker-inline.
So MockMaker is actually the file extension (no txt, properties or any other extension needed).

I also encountered the same issue.
This worked for me: How to use the Mockito's inline mock maker: Option 2
Before Mockito can be used for mocking final classes and methods, it needs to be configured. Based on your screenshot of your project tree, it seems that the location of MockMaker file is incorrect.
Create (if the file still does not exist) or update MockMaker file in the path below
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
... and add this line mock-maker-inline.

If you have multiple modules in project check out if they also have some references to Mockito. For me the problem was deprecated and unnecessary definition in some other small and forgotten library module:
testCompile 'org.mockito:mockito-all:1.10.19'
Removing this unnecessary declaration solved the problem for me

After following configuration instruction, I still wasn't able to get it working.
For me it was due to JDK I was using. After switching to a different one (different provider) the solution with org.mockito.plugins.MockMaker file worked.
I went from JDK with Hotswap agent (trava-jdk-11-dcevm / dcevm-11.0.11+1) to Eclipse adoptOpenJDK (temurin-11.0.14).

This solution worked for me:
Instead of
testCompile "org.mockito:mockito-android:2.9.0"
in the gradle file, replace it with
testCompile group: 'org.mockito', name: 'mockito-inline', version: '2.9.0'
and it would work.

Related

How to detect a nested Unit Test classes (not under test folder) in Java

I know that it's quite hard to test many features of Java language. For example, it would be impossible to test a private variables of a class or similar methods.
I generally tackle this by making a nested class, where this nested class is a unit test, such that :
public class MyClass{
private String somePrivate;
// omitted for brevity
#RunWith(MockitoJUnitRunner.class)
public static class MyClassUnitTest{
#InjectMockito
MyClass myclassMocked;
// so forth...
}
}
thus no need for reflection/powermock or others!
This structure helps me to test all unreachable members or methods of a class.
But it appears that i also should make an automated build where maven will look up this nested classes for unit tests and run it when i mvn clean test in the deployment.
I've been trying to find any answer on this but to no avail i couldn't find any spec of maven or maven-surefire-plugin to say that 'hey please look at these nested classes in the src/main folder and mark them as unit test'. Also, i am using springboot to package all of my project (thus most of the dependencies are with spring)
Anyone up for solution?
For example, it would be impossible to test a private variables of a class or similar methods
You don't need to do this - private methods are private, and they are indirectly tested by testing methods that use them.
You should not be embedding test code or libs such that they have to ship with production software, period.
Don't do what you are proposing.
Edit based on your comment:
As for how you would do it technically, Maven only supports 1 directory for test sources.
You could do something like create an integration test setup that would find the tests in your src/main directory, but the reason this is not easy to do with Maven is because Maven promotes sane patterns and your pattern is not one of those.
Howto add another test source folder to Maven and compile it to a separate folder?

How can Junit test classes detect classes defined in a kotlin file?

I have a Java gradle project in which i'm using a kotlin file to easily define pojo-like classes on one line. However, when i try to start a junit5 test I get a compile error stating that it can't detect any of the classes defined in the kotlin file. I have the kotlin plugin included in the gradle.build file. How do i get the test classes to detect the classes defined in the kotlin file?
Thank you
If you write JUnit5-tests with Gradle, be sure to have something like the following in place in your build.gradle-(or build.gradle.kts)-file, so that Gradle also knows that it should use the JUnit5-platform (compare also JUnit 5 User Guide - Build support - Gradle):
tasks.withType<Test> {
useJUnitPlatform()
}
Moreover also ensure that the annotation you use is the following:
org.junit.jupiter.api.Test
and not the one of JUnit4, i.e. org.junit.Test. If you have that one, you probably want to remove the junit4-dependency altogether.

Using JUnit with Groovy classloader

I have a simple Groovy test class using JUnit
class GroovyJunitTest {
#Test
void test() {
println this.class.getClassLoader().toString()
}
}
This prints out
sun.misc.Launcher$AppClassLoader#4e25154f
which means that Java classloader is used.
I run the test with both Intellij IDE and using gradle test. The same result is in both cases.
Is there a way to configure JUnit to use Groovy classloader?
Once I had the same problem and I used https://github.com/bitstrings/junit-clptr
to load my custom classloader.
Note: this is no longer supported by the crator however for me it did work out. Anyway if it's to important to trust an outside source you can take it as an inspiration and define your own annotation that will let you choose your custom classloader

SpringJUnit4ClassRunner isn't recognized as a type

My aplication uses Spring 4.3.1 and JUnit 4.12.
I have been trying test my methods with JUnit test cases.
I placed the #RunWith(SpringJUnit4ClassRunner.class) annotation in my test class, but eclipse complains "Class cannot be resolved to a type".
I read that this was enough for Eclipse to recognize SpringJUnit4ClassRunner, but isn't.
Is there Anything else remaining to do?
Try to:
Add spring-starter-test dependency through your configuration file & include relevant import statements in test files.
Delete local dependency repository & force new build to your application.
Adding the dependency spring-test the problem has been solved.
I didn't know there was need for this additional dependence, but someone told me here. Thank you.
I initially said that this measure was not enough because I had written the dependency version name wrongly and Maven was not finding the resource. But now it works!

How to get Powermock to work with Dexmaker

I am trying to incorporate Powermock as a dependency for my Android tests using the following build.gradle configuration:
dependencies{
compile 'com.android.support:appcompat-v7:21.0.+'
androidTestCompile('org.mockito:mockito-core:1.9.5')
androidTestCompile('com.google.dexmaker:dexmaker:1.2')
androidTestCompile('com.google.dexmaker:dexmaker-mockito:1.2')
androidTestCompile('org.powermock:powermock-module-junit4:1.5.5') {
exclude module: 'junit'
}
androidTestCompile('org.powermock:powermock-api-mockito:1.5.5') {
exclude module: 'mockito-all'
}
}
However, the compiler is complaining that
Error:Gradle: Execution failed for task ':app:packageDebugTest'.
> Duplicate files copied in APK mockito-extensions/org.mockito.plugins.MockMaker
File 1: ~/.gradle/caches/modules-2/files-2.1/com.google.dexmaker/dexmaker-mockito/1.2/b99884a4c6ef6335ba376f79aa79632b2421c17c/dexmaker-mockito-1.2.jar
File 2: ~/.gradle/caches/modules-2/files-2.1/com.google.dexmaker/dexmaker-mockito/1.2/b99884a4c6ef6335ba376f79aa79632b2421c17c/dexmaker-mockito-1.2.jar
Looking into the jar structure, I noticed that both Dexmaker and Powermock declare a MockMaker in mockito-extensions
What is a MockMaker? How do they differ?
And the most important question: Is it possible to get Powermock to work nicely with Dexmaker?
Thanks in advance. Any help would be greatly appreciated.
MockMaker is a glue module that integrates dexmaker with Mockito. It does what's necessary for Mockito to generate concrete classes with Dalvik .dex files instead of JVM .class files.
It's possible that Powermock will work with Dexmaker, but it's unlikely advanced Powermock features will work. In particular, Powermock advertises this:
PowerMock uses a custom classloader and bytecode manipulation to enable mocking of static methods, constructors, final classes and methods, private methods, removal of static initializers and more.
That custom class loader is unlikely to work on dalvikvm.
you can try to put this in your build.gradle, it solved to me the same problem
android{
...
packagingOptions{
exclude 'mockito-extensions/org.mockito.plugins.MockMaker'
}
...
}
I had the same problem and i just found the solution here. It involves a bit of manual work and you will have to modify the jar file yourself.
So what nparihar suggests is the following.
Make backup copy of powermock-api-mockito-1.5.5.jar
Rename powermock-api-mockito-1.5.5.jar to powermock-api-mockito-1.5.5.zip
Unzip powermock-api-mockito-1.5.5.zip
cd powermock-api-mockito-1.5.5/
rm -rf mockito-extensions
jar cf powermock-api-mockito-1.5.5.jar META-INF/ org/
put the new jar in your libs foler.
This solution worked for me. Let me know if it works for you as well.
Also, i can see that we are using the same dependencies. In my case i had to remove manually the hamcrest.jar and the objenesis.jar as there were confilcts.
Hope that helps.

Categories

Resources