I'm working with a large test suite for an old Java codebase. Long story short, it uses DBUnit to upload static read-only datasets from the local harddisk. At present this is being done on the per-test level, which means the suite takes a ridiculously long time to run.
I'm trying to make a shared static class to be shared at the suite-level. (We also didn't have a proper test suite defined -- I made one using ClasspathSuite)
Another wrinkle is that all of are tests are using #RunWith(PowerMockRunner.class) -- so there's occasionally classpath issues mucking up what I think would normally solve things.
Here's a simple case of what's not working.
Java Code Under Test
Static Dependency in Codebase
package com.somecorp.proj;
public class SomeDependency {
public static String getStaticString() {
// some resource intensive process we don't want running in unit tests
return "real value";
}
}
Class Under Test 1
package com.somecorp.proj;
public class UnderTest {
public String getIt() {
return "Here is the value: " + SomeDependency.getStaticString();
}
}
Class Under Test 2
package com.somecorp.proj;
public class AlsoUnderTest {
public String getTheThing() {
return "some other value using it: " + SomeDependency.getStaticString();
}
}
JUnit Code
Code with init method I want run only ONCE at the start of the suite run
package com.somecorp.proj.testClasses;
public class StaticTestClassRequiringInitialization {
private static String testString;
public static void init() {
// Some expensive stuff
System.out.println("EXPENSIVE INITIALIZATION");
testString = "a test string";
}
public static String getTestString() {
return testString;
}
}
Test 1
package com.somecorp.proj;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.somecorp.proj.testClasses.StaticTestClassRequiringInitialization;
#RunWith(PowerMockRunner.class)
#PrepareForTest(SomeDependency.class)
public class TestUnderTest {
#Before
public void setUp() {
PowerMockito.mockStatic(SomeDependency.class);
PowerMockito.when(SomeDependency.getStaticString()).
thenReturn(StaticTestClassRequiringInitialization.getTestString());
}
#Test
public void testGetIt() {
UnderTest ut = new UnderTest();
assertEquals(
"Here is the value: a test string",
ut.getIt()
);
}
}
Test 2
package com.somecorp.proj;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.somecorp.proj.testClasses.StaticTestClassRequiringInitialization;
#RunWith(PowerMockRunner.class)
#PrepareForTest(SomeDependency.class)
public class TestAlsoUnderTest {
#Before
public void setUp() {
PowerMockito.mockStatic(SomeDependency.class);
PowerMockito.when(SomeDependency.getStaticString()).
thenReturn(StaticTestClassRequiringInitialization.getTestString());
}
#Test
public void testGetTheThing() {
AlsoUnderTest ut = new AlsoUnderTest();
assertEquals(
"some other value using it: a test string",
ut.getTheThing()
);
}
}
Test Suite
package com.somecorp.proj;
import static org.junit.extensions.cpsuite.SuiteType.RUN_WITH_CLASSES;
import static org.junit.extensions.cpsuite.SuiteType.TEST_CLASSES;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.extensions.cpsuite.ClasspathSuite.BeforeSuite;
import org.junit.extensions.cpsuite.ClasspathSuite.ClassnameFilters;
import org.junit.extensions.cpsuite.ClasspathSuite.SuiteTypes;
import org.junit.runner.RunWith;
import com.somecorp.proj.testClasses.StaticTestClassRequiringInitialization;
#RunWith(ClasspathSuite.class)
#SuiteTypes({RUN_WITH_CLASSES, TEST_CLASSES})
#ClassnameFilters({".*Test.*"})
public class ProjectJUnitSuite {
#BeforeSuite
public static void setUpBeforeSuite() {
StaticTestClassRequiringInitialization.init();
}
}
JAR details
powermock-mockito-1.4.12-full.jar
mockito-all-1.9.0.jar
cpsuite-1.2.6.jar
javassist-3.16.1-GA.jar
Using Junit 4.8.1
And the trace of the test failure (notably not an error - a failure) (for one test...2nd one is pretty much identical):
org.junit.ComparisonFailure: expected:<...her value using it: [a test string]> but was:<...her value using it: [null]>
at org.junit.Assert.assertEquals(Assert.java:123)
at org.junit.Assert.assertEquals(Assert.java:145)
at com.somecorp.proj.TestAlsoUnderTest.testGetTheThing(TestAlsoUnderTest.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
at java.lang.reflect.Method.invoke(Method.java:611)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:112)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:73)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:102)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:42)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.junit.extensions.cpsuite.ClasspathSuite.run(ClasspathSuite.java:196)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
How might I get this shared static initializer to run once per suite and be referenceable from all of my Powermock-enabled unit tests?
I got the initialization to occur only once by using the #PowerMockIgnore annotation on all the test classes referencingStaticTestClassRequiringInitialization.
In this case in particular adding the below annotation to both of the JUnit test classes would do the trick.
#PowerMockIgnore("com.somecorp.proj.testClasses.StaticTestClassRequiringInitialization")
I had tried this before, and it had not initially worked because the arguments I had initially passed had been either:
Too broad - Classes that needed to be mocked were no longer being loaded by powermock classloader because I passed an entire package to PowerMockIgnore
Not broad enough - A few dependencies more complex than the example above were present requiring me to ignore some other classes. The result were some strange class loading mismatch errors.
This won't work in all cases, (in particular it won't work if the StaticTestClassRequiringInitialization uses classes that are also being mocked in the test), but it does work in this case.
I had also done some investigation into the PowerMockAgent to avoid the PowerMockRunner, and therefore many of the associated Classloading issues altogether, but the production code under test* required suppressing static initalizers, so it was a non-starter.
*Apologies for not sharing the full source, such are the perils of asking questions for large proprietary codebases.
Related
I have implemented the following test class to test my service class of the REST api, which I have implemented. This is how my ExpertsServiceTest.java class looks like:
package demo;
import lombok.RequiredArgsConstructor;
import org.bson.types.ObjectId;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
#RunWith(SpringRunner.class)
#RequiredArgsConstructor
public class ExpertServiceTest {
#MockBean
private ExpertRepository repository;
#Autowired
private ExpertsServiceImpl service = new ExpertsServiceImpl(repository);
Experts demoExpert = new Experts(ObjectId.get(),"Steve Jobs", "Enterpreneur",
Availability.BUSY, Language.CHINESE);
#Before
public void setUp() throws Exception{
ExpertsServiceImpl service = new ExpertsServiceImpl(repository);
service.deleteAll();
service.createExpert(demoExpert);
}
#After
public void tearDown() throws Exception{
service.deleteAll();
}
public void testCreateExpert(){
Experts expert = new Experts(ObjectId.get(),"Andrea Test", "Software Engineer",
Availability.BUSY, Language.CHINESE);
service.createExpert(expert);
List<Experts> experts = repository.findAll();
assertEquals(2, experts.size());
}
}
However when I run the test I get the following error:
/usr/lib/jvm/jdk-11.0.6/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/opt/idea/lib/idea_rt.jar=43811:/opt/idea/bin -Dfile.encoding=UTF-8 -classpath /opt/idea/lib/idea_rt.jar:/opt/idea/plugins/junit/lib/junit5-rt.jar:/opt/idea/plugins/junit/lib/junit-rt.jar:/home/andrea/Documents/repos/temp/demo_api/target/test-classes:/home/andrea/Documents/repos/temp/demo_api/target/classes:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-data-jpa/2.2.4.RELEASE/spring-boot-starter-data-jpa-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-aop/2.2.4.RELEASE/spring-boot-starter-aop-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-aop/5.2.3.RELEASE/spring-aop-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/aspectj/aspectjweaver/1.9.5/aspectjweaver-1.9.5.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-jdbc/2.2.4.RELEASE/spring-boot-starter-jdbc-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/com/zaxxer/HikariCP/3.4.2/HikariCP-3.4.2.jar:/home/andrea/.m2/repository/org/springframework/spring-jdbc/5.2.3.RELEASE/spring-jdbc-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.1/jakarta.activation-api-1.2.1.jar:/home/andrea/.m2/repository/jakarta/persistence/jakarta.persistence-api/2.2.3/jakarta.persistence-api-2.2.3.jar:/home/andrea/.m2/repository/jakarta/transaction/jakarta.transaction-api/1.3.3/jakarta.transaction-api-1.3.3.jar:/home/andrea/.m2/repository/org/hibernate/hibernate-core/5.4.10.Final/hibernate-core-5.4.10.Final.jar:/home/andrea/.m2/repository/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final.jar:/home/andrea/.m2/repository/org/javassist/javassist/3.24.0-GA/javassist-3.24.0-GA.jar:/home/andrea/.m2/repository/net/bytebuddy/byte-buddy/1.10.6/byte-buddy-1.10.6.jar:/home/andrea/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/home/andrea/.m2/repository/org/jboss/jandex/2.1.1.Final/jandex-2.1.1.Final.jar:/home/andrea/.m2/repository/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar:/home/andrea/.m2/repository/org/dom4j/dom4j/2.1.1/dom4j-2.1.1.jar:/home/andrea/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.1.0.Final/hibernate-commons-annotations-5.1.0.Final.jar:/home/andrea/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.2/jaxb-runtime-2.3.2.jar:/home/andrea/.m2/repository/org/glassfish/jaxb/txw2/2.3.2/txw2-2.3.2.jar:/home/andrea/.m2/repository/com/sun/istack/istack-commons-runtime/3.0.8/istack-commons-runtime-3.0.8.jar:/home/andrea/.m2/repository/org/jvnet/staxex/stax-ex/1.8.1/stax-ex-1.8.1.jar:/home/andrea/.m2/repository/com/sun/xml/fastinfoset/FastInfoset/1.2.16/FastInfoset-1.2.16.jar:/home/andrea/.m2/repository/org/springframework/data/spring-data-jpa/2.2.4.RELEASE/spring-data-jpa-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/data/spring-data-commons/2.2.4.RELEASE/spring-data-commons-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-orm/5.2.3.RELEASE/spring-orm-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-context/5.2.3.RELEASE/spring-context-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-tx/5.2.3.RELEASE/spring-tx-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-beans/5.2.3.RELEASE/spring-beans-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-aspects/5.2.3.RELEASE/spring-aspects-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-hateoas/2.2.4.RELEASE/spring-boot-starter-hateoas-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/hateoas/spring-hateoas/1.0.3.RELEASE/spring-hateoas-1.0.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/plugin/spring-plugin-core/2.0.0.RELEASE/spring-plugin-core-2.0.0.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.2.4.RELEASE/spring-boot-starter-web-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter/2.2.4.RELEASE/spring-boot-starter-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot/2.2.4.RELEASE/spring-boot-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.2.4.RELEASE/spring-boot-autoconfigure-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.2.4.RELEASE/spring-boot-starter-logging-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/home/andrea/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/home/andrea/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1.jar:/home/andrea/.m2/repository/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar:/home/andrea/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/home/andrea/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/home/andrea/.m2/repository/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.2.4.RELEASE/spring-boot-starter-json-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.10.2/jackson-databind-2.10.2.jar:/home/andrea/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.10.2/jackson-annotations-2.10.2.jar:/home/andrea/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.2/jackson-datatype-jdk8-2.10.2.jar:/home/andrea/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.2/jackson-datatype-jsr310-2.10.2.jar:/home/andrea/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.2/jackson-module-parameter-names-2.10.2.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.2.4.RELEASE/spring-boot-starter-tomcat-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.30/tomcat-embed-core-9.0.30.jar:/home/andrea/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/9.0.30/tomcat-embed-el-9.0.30.jar:/home/andrea/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.30/tomcat-embed-websocket-9.0.30.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-validation/2.2.4.RELEASE/spring-boot-starter-validation-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2.jar:/home/andrea/.m2/repository/org/hibernate/validator/hibernate-validator/6.0.18.Final/hibernate-validator-6.0.18.Final.jar:/home/andrea/.m2/repository/org/springframework/spring-web/5.2.3.RELEASE/spring-web-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-webmvc/5.2.3.RELEASE/spring-webmvc-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-expression/5.2.3.RELEASE/spring-expression-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/com/h2database/h2/1.4.200/h2-1.4.200.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-data-mongodb/2.2.4.RELEASE/spring-boot-starter-data-mongodb-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/mongodb/mongodb-driver/3.11.2/mongodb-driver-3.11.2.jar:/home/andrea/.m2/repository/org/mongodb/bson/3.11.2/bson-3.11.2.jar:/home/andrea/.m2/repository/org/mongodb/mongodb-driver-core/3.11.2/mongodb-driver-core-3.11.2.jar:/home/andrea/.m2/repository/org/springframework/data/spring-data-mongodb/2.2.4.RELEASE/spring-data-mongodb-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-starter-test/2.2.4.RELEASE/spring-boot-starter-test-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-test/2.2.4.RELEASE/spring-boot-test-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/boot/spring-boot-test-autoconfigure/2.2.4.RELEASE/spring-boot-test-autoconfigure-2.2.4.RELEASE.jar:/home/andrea/.m2/repository/com/jayway/jsonpath/json-path/2.4.0/json-path-2.4.0.jar:/home/andrea/.m2/repository/net/minidev/json-smart/2.3/json-smart-2.3.jar:/home/andrea/.m2/repository/net/minidev/accessors-smart/1.2/accessors-smart-1.2.jar:/home/andrea/.m2/repository/org/ow2/asm/asm/5.0.4/asm-5.0.4.jar:/home/andrea/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.2/jakarta.xml.bind-api-2.3.2.jar:/home/andrea/.m2/repository/org/junit/jupiter/junit-jupiter/5.5.2/junit-jupiter-5.5.2.jar:/home/andrea/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.5.2/junit-jupiter-api-5.5.2.jar:/home/andrea/.m2/repository/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar:/home/andrea/.m2/repository/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar:/home/andrea/.m2/repository/org/junit/platform/junit-platform-commons/1.5.2/junit-platform-commons-1.5.2.jar:/home/andrea/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.5.2/junit-jupiter-params-5.5.2.jar:/home/andrea/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.5.2/junit-jupiter-engine-5.5.2.jar:/home/andrea/.m2/repository/org/junit/platform/junit-platform-engine/1.5.2/junit-platform-engine-1.5.2.jar:/home/andrea/.m2/repository/org/mockito/mockito-junit-jupiter/3.1.0/mockito-junit-jupiter-3.1.0.jar:/home/andrea/.m2/repository/org/assertj/assertj-core/3.13.2/assertj-core-3.13.2.jar:/home/andrea/.m2/repository/org/hamcrest/hamcrest/2.1/hamcrest-2.1.jar:/home/andrea/.m2/repository/org/mockito/mockito-core/3.1.0/mockito-core-3.1.0.jar:/home/andrea/.m2/repository/net/bytebuddy/byte-buddy-agent/1.10.6/byte-buddy-agent-1.10.6.jar:/home/andrea/.m2/repository/org/objenesis/objenesis/2.6/objenesis-2.6.jar:/home/andrea/.m2/repository/org/skyscreamer/jsonassert/1.5.0/jsonassert-1.5.0.jar:/home/andrea/.m2/repository/com/vaadin/external/google/android-json/0.0.20131108.vaadin1/android-json-0.0.20131108.vaadin1.jar:/home/andrea/.m2/repository/org/springframework/spring-core/5.2.3.RELEASE/spring-core-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-jcl/5.2.3.RELEASE/spring-jcl-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/springframework/spring-test/5.2.3.RELEASE/spring-test-5.2.3.RELEASE.jar:/home/andrea/.m2/repository/org/xmlunit/xmlunit-core/2.6.3/xmlunit-core-2.6.3.jar:/home/andrea/.m2/repository/com/github/fakemongo/fongo/2.2.0-RC2/fongo-2.2.0-RC2.jar:/home/andrea/.m2/repository/org/mozilla/rhino/1.7.7.1/rhino-1.7.7.1.jar:/home/andrea/.m2/repository/com/vividsolutions/jts/1.13/jts-1.13.jar:/home/andrea/.m2/repository/de/grundid/opendatalab/geojson-jackson/1.2/geojson-jackson-1.2.jar:/home/andrea/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.10.2/jackson-core-2.10.2.jar:/home/andrea/.m2/repository/com/lordofthejars/nosqlunit-mongodb/0.7.6/nosqlunit-mongodb-0.7.6.jar:/home/andrea/.m2/repository/com/lordofthejars/nosqlunit-core/0.7.6/nosqlunit-core-0.7.6.jar:/home/andrea/.m2/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/home/andrea/.m2/repository/com/googlecode/lambdaj/lambdaj/2.3.3/lambdaj-2.3.3.jar:/home/andrea/.m2/repository/cglib/cglib-nodep/2.2/cglib-nodep-2.2.jar:/home/andrea/.m2/repository/org/jooq/joor/0.9.3/joor-0.9.3.jar:/home/andrea/.m2/repository/org/codehaus/jackson/jackson-mapper-asl/1.8.8/jackson-mapper-asl-1.8.8.jar:/home/andrea/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.8.8/jackson-core-asl-1.8.8.jar:/home/andrea/.m2/repository/org/mongodb/mongo-java-driver/3.11.2/mongo-java-driver-3.11.2.jar:/home/andrea/.m2/repository/com/foursquare/fongo/1.0.7/fongo-1.0.7.jar:/home/andrea/.m2/repository/org/hamcrest/hamcrest-core/2.1/hamcrest-core-2.1.jar:/home/andrea/.m2/repository/org/hamcrest/hamcrest-library/2.1/hamcrest-library-2.1.jar:/home/andrea/.m2/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:/home/andrea/.m2/repository/org/projectlombok/lombok/1.18.10/lombok-1.18.10.jar:/home/andrea/.m2/repository/de/flapdoodle/embed/de.flapdoodle.embed.mongo/2.2.0/de.flapdoodle.embed.mongo-2.2.0.jar:/home/andrea/.m2/repository/de/flapdoodle/embed/de.flapdoodle.embed.process/2.1.2/de.flapdoodle.embed.process-2.1.2.jar:/home/andrea/.m2/repository/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar:/home/andrea/.m2/repository/net/java/dev/jna/jna/4.5.2/jna-4.5.2.jar:/home/andrea/.m2/repository/net/java/dev/jna/jna-platform/4.5.2/jna-platform-4.5.2.jar:/home/andrea/.m2/repository/org/apache/commons/commons-compress/1.18/commons-compress-1.18.jar:/home/andrea/.m2/repository/junit/junit/4.12/junit-4.12.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 demo.ExpertServiceTest,testCreateExpert
java.lang.Exception: No runnable methods
at org.junit.runners.BlockJUnit4ClassRunner.validateInstanceMethods(BlockJUnit4ClassRunner.java:191)
at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:128)
at org.junit.runners.ParentRunner.validate(ParentRunner.java:416)
at org.junit.runners.ParentRunner.<init>(ParentRunner.java:84)
at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.<init>(SpringJUnit4ClassRunner.java:137)
at org.springframework.test.context.junit4.SpringRunner.<init>(SpringRunner.java:49)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:36)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Process finished with exit code 255
Can someone indentify why this happens? I really cannot understand how to go about resolve this, as I am new to spring boot. Thanks in advance for your help
#Test annotation is missing in your method.
According to me the easiest fix would be adding the same, as mentioned below.
#Test
public void testCreateExpert(){
Experts expert = new Experts(ObjectId.get(),"Andrea Test", "Software Engineer",
Availability.BUSY, Language.CHINESE);
service.createExpert(expert);
List<Experts> experts = repository.findAll();
assertEquals(2, experts.size());
}
put #Test annotation on testCreateExpert() method.
Prefixing test methods with test was a practice from JUnit 3.x. As you seem to be using JUnit 4.x, you no longer need that. However, you must annotate your tests methods with #Test:
The Test annotation tells JUnit that the public void method to which it is attached can be run as a test case. To run the method, JUnit first constructs a fresh instance of the class then invokes the annotated method.
So, you'll have the following:
#Test
public void testCreateExpert() {
...
}
I am writing a unit test to mock a static method in the verticle but getting ClassNotPreparedException always. I think that its only possible to mock this way if only the class is static, but i have non static class. What am i missing?
I have tried various solutions like using #rule or #PowerMockIgnore
//myVerticleTest.java
package com.blabla.me.verticles;
import static com.google.common.truth.Truth.assertThat;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import io.vertx.core.Vertx;
import io.vertx.junit5.VertxTestContext;
import io.vulpx.VulpxTestBase;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.junit.runner.RunWith;
import com.blabla.me.verticles.AdditionalInformationCardVerticle;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.junit.Rule;
import com.blabla.me.verticles.st;
#RunWith(PowerMockRunner.class)
#PrepareForTest({ st.class })
#PowerMockIgnore({"org.mockito.*"})
public class myVerticleTest extends VulpxTestBase {
#Rule public PowerMockRule rule = new PowerMockRule();
private Vertx vertx;
private AdditionalInformationCardVerticle dummy;
#BeforeEach
#PrepareForTest({ st.class })
public void setUp(VertxTestContext testContext) throws Exception {
vertx = Vertx.vertx();
try {
PowerMockito.mockStatic(st.class);
PowerMockito.when(st.createClient()).thenReturn("kk");
//deploying verticle
dummy = new AdditionalInformationCardVerticle();
vertx.deployVerticle(dummy, testContext.completing());
} catch (Exception e) {
System.out.println("heyyy eroorrr : " + e);
}
}
#Test
#PrepareForTest({ st.class })
public void justnormaltest() {
cla ownclass = new cla();
String k = ownclass.createfromclass();
assertThat("kk").isEqualTo(k);
}
}
// st.java
public class st {
public static String createClient() {
return "kk";
}
}
// cla.java
public class cla {
public String createfromclass() {
return st.createClient();
}
}
I expect it to run the assertion but i always get below excpetion:
"org.powermock.api.mockito.ClassNotPreparedException:
The class com.sap.me.verticles.st not prepared for test.
To prepare this class, add class to the '#PrepareForTest' annotation.
In case if you don't use this annotation, add the annotation on class or method level. "
Here:
#PrepareForTest({ st.class })
That one goes to exactly one place: in front of your test class public class myVerticleTest.
And hint: instead of adding more and more "things" to not working code: pick any good documentation, and try to follow that to the last ; in the example code (instead of assuming that adding more and more things here or there would help).
One good starting point: the official documentation on static mocking.
And of course, the usual caveat: consider not learning about PowerMock in the first place. Instead focus on writing "easy to test" code. Far too often, people think PowerMock(ito) is the answer to their problem. When their problem in reality is their inability to write "easy to test" production code.
I have a spring-boot application where my #SpringBootApplication starter class looks like a standard one. So I created many tests for all my functionalities and send the summary to sonarqube to see my coverage.
For my starter class Sonarqube tells me that I just have 60% coverage. So the average coverage is not good as expected.
My Test class is just the default one.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = ElectronicGiftcardServiceApplication.class)
public class ElectronicGiftcardServiceApplicationTests {
#Test
public void contextLoads() {
}
}
So how can I test my main class in the starter class of my application?
All these answers seem overkill.
You don't add tests to make a metric tool happy.
Loading a Spring context of the application takes time. Don't add it in each developer build just to win about 0.1% of coverage in your application.
Here you don't cover only 1 statement from 1 public method. It represents nothing in terms of coverage in an application where thousands of statements are generally written.
First workaround : make your Spring Boot application class with no bean declared inside. If you have them, move them in a configuration class (for make them still cover by unit test). And then ignore your Spring Boot application class in the test coverage configuration.
Second workaround : if you really need to to cover the main() invocation (for organizational reasons for example), create a test for it but an integration test (executed by an continuous integration tool and not in each developer build) and document clearly the test class purpose :
import org.junit.Test;
// Test class added ONLY to cover main() invocation not covered by application tests.
public class MyApplicationIT {
#Test
public void main() {
MyApplication.main(new String[] {});
}
}
You can do something like this
#Test
public void applicationContextLoaded() {
}
#Test
public void applicationContextTest() {
mainApp.main(new String[] {});
}
I solved in a different way here. Since this method is there only as a bridge to Spring's run, I annotated the method with #lombok.Generated and now sonar ignores it when calculating the test coverage.
Other #Generated annotations, like javax.annotation.processing.Generated or javax.annotation.Generated might also work but I can't test now because my issue ticket was closed.
package com.stackoverflow;
import lombok.Generated;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
#Generated
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
I had the same goal (having a test that runs the main() method) and I noticed that simply adding a test method like #fg78nc said will in fact "start" the application twice : once by spring boot test framework, once via the explicit invocation of mainApp.main(new String[] {}), which I don't find elegant.
I ended up writing two test classes : one with #SpringBootTest annotation and the empty test method applicationContextLoaded(), another one without #SpringBootTest (only RunWith(SpringRunner.class)) that calls the main method.
SpringBootApplicationTest
package example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.boot.test.context.SpringBootTest;
#RunWith(SpringRunner.class)
#SpringBootTest
public class SpringBootApplicationTest {
#Test
public void contextLoads() {
}
}
ApplicationStartTest
package example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
public class ApplicationStartTest {
#Test
public void applicationStarts() {
ExampleApplication.main(new String[] {});
}
}
Overall, the application is still started two times, but because there is now two test classes. Of course, with only these two tests methods, it seems overkill, but usually more tests will be added to the class SpringBootApplicationTest taking advantage of #SpringBootTest setup.
In addition to the answers above, here is a unit test of a SpringBoot application's main method for if you are using JUnit 5 and Mockito 3.4+:
try (MockedStatic<SpringApplication> mocked = mockStatic(SpringApplication.class)) {
mocked.when(() -> { SpringApplication.run(ElectronicGiftCardServiceApplication.class,
new String[] { "foo", "bar" }); })
.thenReturn(Mockito.mock(ConfigurableApplicationContext.class));
ElectronicGiftCardServiceApplication.main(new String[] { "foo", "bar" });
mocked.verify(() -> { SpringApplication.run(ElectronicGiftCardServiceApplication.class,
new String[] { "foo", "bar" }); });
}
It verifies that the static method run() on the SpringApplication class is called with the expected String array when we call ElectronicGiftCardServiceApplication.main().
Same idea as awgtek and Ramji Sridaran, but their solutions are for JUnit 4.
You can Mock SpringApplication since that is a dependency of the method under test. See how here.
I.e.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;
#RunWith(PowerMockRunner.class)
public class ElectronicGiftcardServiceApplicationTest {
#Test
#PrepareForTest(SpringApplication.class)
public void main() {
mockStatic(SpringApplication.class);
ElectronicGiftcardServiceApplication.main(new String[]{"Hello", "World"});
verifyStatic(SpringApplication.class);
SpringApplication.run(ElectronicGiftcardServiceApplication.class, new String[]{"Hello", "World"});
}
}
Using junit
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.boot.SpringApplication;
import static org.assertj.core.api.Assertions.*;
class WebsiteApplicationTests {
#Test
void testApplication() {
MockedStatic<SpringApplication> utilities = Mockito.mockStatic(SpringApplication.class);
utilities.when((MockedStatic.Verification) SpringApplication.run(WebsiteApplication.class, new String[]{})).thenReturn(null);
WebsiteApplication.main(new String[]{});
assertThat(SpringApplication.run(WebsiteApplication.class, new String[]{})).isEqualTo(null);
}
}
Add these dependencies in pom.xml
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>your.awesome.package.Application</mainClass>
</configuration>
</plugin>
If you aim for 100% coverage, one thing you can do is simply not having a main method at all. You still require a class annotated with #SpringBootApplication but it can be empty.
Be warned though as it has its drawbacks and other tools that rely on main can break.
This simple mock test for SpringApplication does not invoke any methods but just tests the starter app. [uses PowerMockRunner.class]
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;
#RunWith(PowerMockRunner.class)
#PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*"})
public class JobsAppStarterTest {
#Test
#PrepareForTest(SpringApplication.class)
public void testSpringStartUp() {
PowerMockito.mockStatic(SpringApplication.class);
SpringApplication.run(JobsAppStarter.class, new String[] {"args"});
JobsAppStarter.main(new String[] {"args"});
}
}
If the idea is to exclude the SpringApplication class from sonar scan (which is the recommended way of doing it), you can exclude it with the following configuration in the build.gradle
plugins {
id 'org.sonarqube' version '3.4.0.2513'
}
sonarqube {
properties {
property "sonar.exclusions", "**/*Application.java"
}
}
Even though this question has been answered extensively I had a use case that is not covered here that is perhaps interesting to share. I am validating some properties at startup and I wanted to assert that the application would fail to start if these properties were configured wrong. In JUnit4 I could have done something like this:
#ActiveProfiles("incorrect")
#SpringBoot
public class NetworkProbeApplicationTest {
#Test(expected=ConfigurationPropertiesBindException.class)
public void contextShouldNotLoadWhenPropertiesIncorrect() {
}
}
But in JUnit5 you can no longer add the "expected" value to your #Test annotation and you have to do it differently. And since I wanted to start the application with an incorrect set of properties I needed to pass in which profile to use as a main() argument. I could not really find this documented anywhere, but passing in arguments through the main() method requires you to prefix your arguments with a double hyphen and separate the key and value with an equals sign. A complete test would look like this:
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindException;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class NetworkProbeApplicationTest {
#Test
public void contextShouldNotLoadWhenPropertiesIncorrect() {
Exception exception = assertThrows(ConfigurationPropertiesBindException.class, () -> {
SpringApplication.run(NetworkProbeApplication.class, "--spring.profiles.active=incorrect");
});
String expectedMessage = "Error creating bean with name 'dnsConfiguration': Could not bind properties to 'DnsConfiguration' : prefix=dns";
assertTrue(exception.getMessage().contains(expectedMessage));
}
}
This question already has answers here:
How do I unit test code which uses Java UUID?
(4 answers)
Closed 5 years ago.
I'm trying to test a Web Service method which connects to a SQL Server Database which contains JCR nodes, as we're using JackRabbit.
The method looks like:
public String addDocumentByJson(String fileName, byte[] fileContent, int status, String userName, String jsonProperties) {
UUID id = UUID.randomUUID();
// It does a bunch of operations here
return jsonResult;
}
Where jsonResult is an object similar to this one:
{
"id" : "<A random UUID>"
"version" : 1
}
Now, when I try to test it following the steps in this answer and the code in this post and I came off with the following code (which as I said is based on the past links):
#PrepareForTest({ UUID.class })
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/TestSpringConfig.xml")
public class TestJackRabbitService {
#Autowired
#Qualifier("jackRabbitService")
IJackRabbitService jackRabbitService;
private byte[] fileContent;
private int versionFile;
public TestJackRabbitService() {
classLoader = getClass().getClassLoader();
}
#BeforeClass
public static void init() {
LOG.trace("Run #BeforeClass");
try {
fileContent = IOUtils.toByteArray(new FileInputStream(new File(Thread.currentThread().getContextClassLoader().getResource("fileTest.txt"))));
} catch (Exception e) {
LOG.error(ExceptionUtils.getStackTrace(e));
}
}
#Before
public void before() {
LOG.trace("Run #Before");
try {
versionFile = jackRabbitService.getLastVersionOf(nameApp, nameFile); //This method returns an int,
} catch (Exception e) {
LOG.error(ExceptionUtils.getStackTrace(e));
}
}
#Test
public void testAddDocumentsByJson() {
//Some tests which run correctly
final UUID uuid = UUID.randomUUID();
mockStatic(UUID.class);
LOG.debug(uuid);
//doReturn(uuid).when(UUID.randomUUID());
when(UUID.randomUUID()).thenReturn(uuid);
idFile = uuid;
assertEquals(jackRabbitService.addDocumentByJson(nameFile, bytes, nameApp, 5, jsonproperties), "{\"id\":\"" + uuid + "\",\"version\":1}");
}
}
However when I test this method it gives me the following results:
Results :
Failed tests:
testAddDocumentsByJson(com.optimissa.test.junit.TestJackRabbitService): expected:<{"id":"[1efaf3b8-ca7c-4e6f-878f-102d9a7a92d9]","version":1}> but was:<{"id":"[cfa1a8b0-be6a-46b1-90f5-d2f6d230796a]","version":1}>
As you can see both UUIDs are different, and from what I read on my first link in this question is that it should return the same UUID everytime the static method UUID.randomUUID() is called (the one stored in the uuid variable inside the TestJackRabbitService class...
I also tried with doReturn method as explained in this answer but it produces the following stack trace:
testAddDocumentsByJson(com.optimissa.test.junit.TestJackRabbitService) Time elapsed: 5.279 sec <<< ERROR!
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.optimissa.test.junit.TestJackRabbitService.testAddDocumentsByJson(TestJackRabbitService.java:143)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:182)
at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:164)
at org.powermock.core.MockGateway.methodCall(MockGateway.java:134)
at com.optimissa.test.junit.TestJackRabbitService.testAddDocumentsByJson(TestJackRabbitService.java:143)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner$2.call(DelegatingPowerMockRunner.java:149)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner$2.call(DelegatingPowerMockRunner.java:141)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.withContextClassLoader(DelegatingPowerMockRunner.java:132)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.run(DelegatingPowerMockRunner.java:141)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:57)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
From this answer I read (but I don't understand) that maybe I need to create a new object from the class I'm trying to test? I'm injecting the dependency at the very beginning of the test class, I'm really new to JUnit testing and english is not my native language, however I can comprehend most of thing but that answer is giving me a hard time understanding it (due to my lack of knowledge in JUnit testing).
How can I make my JUnit test to retrieve the same ID which is generated inside the method (or intercept the call to UUID.randomUUD() to return the value inside my JUnit test) ?
Edit
After trying #hammerfest's answer, with the following changes:
UUID uuid = PowerMockito.mock(UUID.class);
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuid);
String jsonToCompare = "{\"id\":\"" + uuid + "\",\"version\":1}";
String jsonFromJRS = jackRabbitService.addDocumentByJson(nameFile, bytes, nameApp, 5, jsonproperties);
assertEquals(jsonFromJRS, jsonToCompare);
I still get this result:
testAddDocumentsByJson(com.optimissa.test.junit.TestJackRabbitService): expected:<{"id":"[493410b3-dd0b-4b78-97bf-289f50f6e74f]","version":1}> but was:<{"id":"[00000000-0000-0000-0000-000000000000]","version":1}>
Common mistake with mocking system classes it's that they are added to #PrepareForTest, but unfortunately it's impossible to mock final Java System classes directly. But PowerMock provides workaround. PowerMock replaces calls to system classes by call to PowerMock class. A class that use final system class should be added to #PrepareForTest
I've added example how to mock UUID.
public class DocumentService {
public JsonDocument saveDocument(JsonDocument document){
UUID uuid = UUID.randomUUID();
document.setId(uuid.toString());
return document;
}
}
Test
#RunWith(PowerMockRunner.class)
#PrepareForTest(DocumentService.class)
public class DocumentServiceTest {
#Test
public void should_set_id() throws Exception {
final String id = "493410b3-dd0b-4b78-97bf-289f50f6e74f";
UUID uuid = UUID.fromString(id);
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuid);
DocumentService documentService = new DocumentService();
JsonDocument document = new JsonDocument();
documentService.saveDocument(document);
assertThat(document.getId())
.as("Id is set")
.isEqualTo(id);
}
}
You may find more in documentation.
I've reused and modified #hammerfest s example a bit which work on my machine.
The first case simply mocks the static invocation of the UUID class and asserts that the returned UUID of the SUT equals the mocked UUID:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.UUID;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
#PrepareForTest({ UUID.class })
#RunWith(PowerMockRunner.class)
public class StaticMockTest {
#Test
public void test() {
MyClass sut = new MyClass();
UUID uuidLocal = UUID.randomUUID();
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID(), is(equalTo(uuidLocal)));
}
private class MyClass {
public UUID getUUID() {
return UUID.randomUUID();
}
}
}
The second case invokes a method of a Spring managed bean which returns the mocked UUID:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
import java.util.UUID;
import javax.annotation.Resource;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
#PrepareForTest({ UUID.class })
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
#BootstrapWith(DefaultTestContextBootstrapper.class)
#ContextConfiguration(classes = {StaticMockTest2.ContextConfig.class},
loader= AnnotationConfigContextLoader.class)
#PowerMockIgnore({"javax.management.*"})
public class StaticMockTest2 {
#Resource
private MyClass sut;
#Test
public void test() {
UUID uuidLocal = UUID.randomUUID();
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID(), is(equalTo(uuidLocal)));
}
private static class MyClass {
public UUID getUUID() {
return UUID.randomUUID();
}
}
#Configuration
public static class ContextConfig {
#Bean
public MyClass myClass() {
return new MyClass();
}
}
}
Both tests work on my machine though I'd suggest to refactor the UUID generation into a utility class which you instantiate and inject via Spring. Then you can simply replace the PowerMock stuff with ordinary Mockito mocking and avoid dealing with such problems:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import java.util.UUID;
import javax.annotation.Resource;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {MockTest3.ContextConfig.class},
loader= AnnotationConfigContextLoader.class)
public class MockTest3 {
#Resource
private Util mockUtil;
#Resource
private MyClass sut;
#Test
public void test() {
UUID uuidLocal = UUID.randomUUID();
when(mockUtil.generateUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID(), is(equalTo(uuidLocal)));
}
private static class MyClass {
private Util util;
public MyClass(Util util) {
this.util = util;
}
public UUID getUUID() {
return util.generateUUID();
}
}
private static class Util {
public UUID generateUUID() {
return UUID.randomUUID();
}
}
#Configuration
public static class ContextConfig {
#Bean
public Util mockUtil() {
return mock(Util.class);
}
#Bean
public MyClass myClass() {
return new MyClass(mockUtil());
}
}
}
If you don't want to rely on Spring for testing (to speed up things even further) you can inject the dependencies yourself either via constructor injection or via Whitebox.setInternalState(sut, "fieldName", mockObject); or Springs ReflectionUtils.setField(sut, "fieldName", mockObject);.
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;
import java.util.UUID;
import javax.annotation.Resource;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
public class MockTest4 {
#Test
public void test() {
Util mockUtil = mock(Util.class);
MyClass sut = new MyClass(mockUtil);
// MyClass sut = new MyClass();
// Whitebox.setInternalState(sut, "util", mockUtil);
UUID uuidLocal = UUID.randomUUID();
when(mockUtil.generateUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID(), is(equalTo(uuidLocal)));
}
private class MyClass {
#Resource
private Util util;
public MyClass() {}
public MyClass(Util util) {
this.util = util;
}
public UUID getUUID() {
return util.generateUUID();
}
}
private class Util {
public UUID generateUUID() {
return UUID.randomUUID();
}
}
}
The last test contains both options, constructor or field injection, you can play with.
Due to #hammerfest s comment I'm adding a further example here that showcases what to do if MyClass is externally defined. Note this example was basically taken from Github before I read the answer of #ArthurZagretdinov, who is probably the author of this test in first place (as pointed out by #hammerfest in the comments). First the standalone MyClass implementation:
import java.util.UUID;
public class MyClass {
public UUID getUUID() {
return UUID.randomUUID();
}
}
Next, the test that uses the external MyClass definition:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.UUID;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
#PrepareForTest({ MyClass.class })
#RunWith(PowerMockRunner.class)
public class StaticMockTest3 {
#Test
public void test() {
MyClass sut = new MyClass();
final String id = "493410b3-dd0b-4b78-97bf-289f50f6e74f";
UUID uuid = UUID.fromString(id);
// UUID uuidLocal = UUID.randomUUID();
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuidLocal);
// when(UUID.randomUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID().toString(), is(equalTo(uuid.toString())));
}
}
If you comment out both comment-lines in the above cenario, you will figure out that the test will fail due to unequal UUIDs. This means that the preparation for MyClass does also respect using the declared UUID mock and thus can be used for mocking static classes.
I am trying to run a quick selenium example to get started, and am having trouble. I have written JUnit test cases before and they work fine, but here's my code and error.
package alltests;
import testsuites.SeleniumTestTutorial;
import junit.framework.Test;
import junit.framework.TestSuite;
public class AllTests {
public static Test suite() {
TestSuite suite = new TestSuite("Test for SeleniumTutorial");
suite.addTestSuite(SeleniumTestTutorial.class);
return suite;
}
}
Here's an old tutorial I am using. I didn't write these tests, they will most likely fail, I'm just trying to get them to run.
package testsuites;
import junit.framework.TestCase;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.Selenium;
public class SeleniumTestTutorial extends TestCase{
private static Selenium browser;
#BeforeClass
public static void init() {
browser = new DefaultSelenium("localhost", 4444, "*firefox",
"http://new.music.yahoo.com/");
browser.start();
}
#Test
public void isVideosHpLoaded() {
browser.open("http://new.music.yahoo.com/");
browser.click("//*[#id=\"YMusic_HeaderNavItemMenu2\"]/a/span");
browser.waitForPageToLoad("5000");
System.out.println("checking title " + browser.getTitle());
assertEquals("Music Videos on Yahoo! Music", browser.getTitle());
}
#Test
public void isTop100VideosModuleLoaded() {
Number numElements = browser
.getXpathCount("//*[#id=\"ymMvHpTopVideos\"]/div/div/h2/a");
String modHeaderText = browser
.getText("//*[#id=\"ymMvHpTopVideos\"]/div/div/h2/a");
assertEquals(1, numElements.intValue());
assertEquals("Top 100 Videos", modHeaderText);
}
#Test
public void isVideoStationsModuleLoaded() {
Number numElements = browser
.getXpathCount("//*[#id=\"ymMvHpVideoStationsContentContainer\"]/div/div[2]/h2/a");
String modHeaderText = browser
.getText("//*[#id=\"ymMvHpVideoStationsContentContainer\"]/div/div[2]/h2/a");
assertEquals(1, numElements.intValue());
assertEquals("Video Stations", modHeaderText);
}
#Test
public void countTotalVideoRecs() {
Number numElements = browser
.getXpathCount("//*[#id=\"ymusicRecommendHp\"]//ul[#class=\"ymusic_thumbnailList\"]/li");
assertEquals(6, numElements.intValue());
}
#AfterClass
public static void cleanup() {
browser.stop();
}
}
Here's the error I am getting. I've used this format for other JUnit tests, and I have never had any problems. I also can't seem to find an updated tutorial for using JUnit and Selenium. If anyone has any good links I would not be opposed. Thanks in advance!
junit.framework.AssertionFailedError: No tests found in testsuites.SeleniumTestTutorial
at junit.framework.Assert.fail(Assert.java:47)
at junit.framework.TestSuite$1.runTest(TestSuite.java:97)
at junit.framework.TestCase.runBare(TestCase.java:134)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:243)
at junit.framework.TestSuite.run(TestSuite.java:238)
at junit.framework.TestSuite.runTest(TestSuite.java:243)
at junit.framework.TestSuite.run(TestSuite.java:238)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
I think it's because you're mixing JUnit 3 and JUnit 4 style tests.
If you're using JUnit 3.x, you want to extend TestCase, as you have, but then you need all your test methods to be named "testXYZ" (i.e. starting with the word test). The #Test annotations are ignored.
For JUnit 4, you don't extend TestCase, and you use the annotations.
You are mixing a JUnit 3 Test Suite and JUnit 4 test classes. You need to create a JUnit 4 test suite:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
#RunWith(Suite.class)
#Suite.SuiteClasses({
SeleniumTestTutorial.class
})
public class AllTests {
}
When you execute a TestSuite (JUnit 3), it searches for methods which start with 'test'. For a JUnit 4 suite (with #RunWith(Suite.class)), then it searches for methods with the #Test annotation.