I'm trying to write a Test that will compare strings' equality.
Here is a code snippet of the class that should be tested
package ge.jibo.util;
public class TextManager {
private String concatenateTwoString(String t1, String t2) {
return t1 t2;
}
public String concatenate(String t1, String t2) {
return concatenateTwoString(t1, t2);
}
}
and here is a test class
package ge.jibo.util;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import static org.assertj.core.api.Assertions.assertThat;
public class TextManagerTest {
@Test
@PrepareForTest(TextManager.class)
void checkIfConcatenationWorks() throws Exception {
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("someText").when(textmanager,"concatenateTwoString", Mockito.anyString(), Mockito.anyString());
String text = textmanager.concatenate("ji","bo");
assertThat(text).isEqualTo("jibo");
}
}
as you see, I want to test the public method concatenate
, which calls the private method concatenateTwoString
in the same class.
The idea is that I want to make a mock object for the private method, and whenever it will be called from the public method it should return the constant value "someText"
but it returns the null
instead of "someText"
from the private method concatenateTwoString
.
org.opentest4j.AssertionFailedError:
Expecting:
<null>
to be equal to:
<"jibo">
but was not.
Expected :jibo
Actual :null
Does anyone know how to fix it?
Dependency Versions:
- junit-jupiter - 5.7.2
- junit-platform-launcher - 1.8.1
- mockito-core - 3.12.4
- mockito-junit-jupiter - 3.12.4
- powermock-core - 2.0.9
- powermock-api-mockito2 - 2.0.9
CodePudding user response:
As @heaprc
mentioned in his post, the "Test" method can be executed correctly, in the case when we use JUnit4
instead of JUnit5
and by adding JUnit4
annotation for powerMock @RunWith(PowerMockRunner.class)
.
Everything is correct, there is no direct solution to mock the private method under the public one in the case of JUnit5.
So what options do we have?
In m.o there is three option in this case:
- leave
JUnit5
for the"Test framework"
and do not try to make a mock of the private method. At first, think about if you really want to test thatprivate
method and if it's necessary to test make itpackage-private
(by changing the method topackage-private
you are breaking the concept of encapsulation, but if you relly need to test it, then I think it's not a big problem) - downgrade your
"Test framework"
fromJUnit5
toJUnit4
. but in this case, if you already wrote 100 tests inJUnit5
you will have to change all of them to work onJUnit4
. The fact that you have to downgrade your framework is really annoying, and the fact that you have to rewrite 100 tests forJUnit4
is very irritant. - make
JUnit4
andJUnit5
work together. I think this is the better solution and here is the example:
you have to have the following dependencies in your pom.xml
file.
pom.xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.8.1</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
junit-vintage-engine
allows you to runJUnit4
test.junit-jupiter-engine
allows you to runJUnit5
test.
Code snippet
import org.junit.Assert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@PrepareForTest(TextManager.class)
@RunWith(PowerMockRunner.class)
public class TextManagerTest {
@org.junit.Test //Test using Junit4
public void checkIfConcatenationWorksWithJunit4() throws Exception {
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("jibo").when(textmanager, "concatenateTwoString", Mockito.anyString(), Mockito.anyString());
String text = textmanager.concatenate("ji", "bo");
Assert.assertEquals(text,"jibo");
}
@Test //Test using Junit5
public void checkIfConcatenationWorksWithJunit5() {
Assertions.assertEquals("text","jibo");
}
}
in this case, you will run the checkIfConcatenationWorksWithJunit4
test method with the Junit4
platform, and @RunWith(PowerMockRunner.class)
will work for this execution.
in the case of checkIfConcatenationWorksWithJunit5
method, it will run with the Junit5(Jupiter) platform.
Finally
you have to add maven-surefire-plugin
in the pom.xml
file to make both JUnit4
and JUnit5
tests to be tested while building it or testing using maven commands
.
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
without adding maven-surefire-plugin
the only one platform (JUnit4)
test will be executed at runtime.
CodePudding user response:
You have forgotten to add @RunWith
and specify the PowerMockRunner
If you are able to use Junit-4 this is the solution.
@RunWith(PowerMockRunner.class)
@PrepareForTest(TextManager.class)
public class TextManagerTest {
@Test
public void checkIfConcatenationWorks() throws Exception {
// Arrange
TextManager textmanager = PowerMockito.spy(new TextManager());
PowerMockito.doReturn("someText").when(textmanager,"concatenateTwoString"
, Mockito.anyString()
, Mockito.anyString());
// Act
String text = textmanager.concatenate("ji","bo");
// Assert
assertThat(text).isEqualTo("jibo");
}
}