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
JUnit5for 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 thatprivatemethod and if it's necessary to test make itpackage-private(by changing the method topackage-privateyou 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"fromJUnit5toJUnit4. but in this case, if you already wrote 100 tests inJUnit5you 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 forJUnit4is very irritant. - make
JUnit4andJUnit5work 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-engineallows you to runJUnit4test.junit-jupiter-engineallows you to runJUnit5test.
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");
}
}
