I am trying to have RunWith(PowerMockRunner.class) working with my existing package annotation.
Versions:
powermock 1.4.12 mockito 1.9.0 junit 4.8.2
package-info.java // this is for the package annotation
#TestAnnotation(version="1.0")
package com.smin.dummy;
TestAnnotation.java // this is the the metadata annotation class for package "com.smin.dummy"
package com.smin.dummy;
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PACKAGE)
public #interface TestAnnotation {
String version();
}
A.java
package com.smin.dummy;
public class A {
private static Package myPackage;
private static TestAnnotation version;
static {
myPackage = TestAnnotation.class.getPackage();
version = myPackage.getAnnotation(TestAnnotation.class);
}
public static String getVersion() {
return version.version();
}
}
MockA.java
package com.smin.dummy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.smin.dummy.A;
#RunWith(PowerMockRunner.class) //comment out this line to see the difference
#PrepareForTest(A.class)
public class MockA {
#Test
public void test_mocked() throws Throwable {
String thisVersion = A.getVersion();
System.out.println(thisVersion);
}
}
In the unitest MockA.java, if I don't use RunWith(PowerMockRunner.class), I will get the thisVersion printed 0.1 as expected. But after adding RunWith(PowerMockRunner.class), thisVersion turns into null. I suspect PowerMockRunner is doing some funny thing with the package annotation here, anybody has any idea? see the mini version of my code below:
Building on #Alban's sleuthing in the comments, it looks like adding this annotation to your test case should circumvent the problem:
#PowerMockIgnore("com.smin.dummy.TestAnnotation")
Related
Intro:
Our product needs to have integrations tests for 3 different databases:
Oracle
Postgres
MSSQL
We are using Spring Boot as our framework and TestContainers to start up the databases mentioned above.
The problem:
We need to run the same tests for each container (database).
After a lot of digging on the net the only way that I could think of was using a BaseClass where we write all the test cases and for each container, we create a class that inherits from the BaseClass and we override the method and annotate it with #Test.
Below in the code, you will a single JUnit5 extension for Postgres that starts a TestContainer, base test class, and a test class that gets extended from the Postgres extension, starts a Spring Application context, and runs the tests from the base class.
The code:
import com.company.itest.AutoConfig;
import com.company.itest.BaseIntegrationTest;
import com.company.itest.db.mssql.MSSqlTest;
import com.company.itest.db.oracle.OracleTest;
import com.company.itest.db.postgres.PostgresTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
public class TestTheTest extends BaseIntegrationTest {
public void contextLoads() {
Assertions.assertEquals(1, 1);
}
public void contextLoads2() {
Assertions.assertNotEquals(1, 2);
}
}
#SpringBootTest(
classes = AutoConfig.class,
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#PostgresTest
class TestPostgres extends TestTheTest {
#Test
public void contextLoads() {
super.contextLoads();
}
#Test
public void contextLoads2() {
super.contextLoads2();
}
}
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.PostgreSQLContainer;
public class PostgresqlTestContainersExtension implements BeforeAllCallback, AfterAllCallback {
private final Logger log = LoggerFactory.getLogger(PostgresqlTestContainersExtension.class);
private PostgreSQLContainer<?> postgres;
#Override
public void beforeAll(ExtensionContext context) {
log.info("Setting up postgres container");
postgres = new PostgreSQLContainer<>("postgres:13").withReuse(true);
postgres.start();
System.setProperty("spring.datasource.url", postgres.getJdbcUrl());
System.setProperty("spring.datasource.username", postgres.getUsername());
System.setProperty("spring.datasource.password", postgres.getPassword());
}
#Override
public void afterAll(ExtensionContext context) {
postgres.stop();
}
}
package com.company.itest.db.postgres;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#ExtendWith(SpringExtension.class)
#ExtendWith({PostgresqlTestContainersExtension.class})
#Testcontainers
public #interface PostgresTest {}
The question:
How can I create a single JUnit test class and then rerun it with a different JUnit5 extension without doing this polymorphism?
If you are using maven, you could try to have a different profile per db
I have a spring project, which contains a class called EnvUtils:
package com.example;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Objects;
#Component
public class EnvUtils {
#Value("${env}")
private String env;
/**
* Develop environment
*/
public boolean isDev() {
return Objects.equals(env, "dev");
}
/**
* Production environment
*/
public boolean isProd() {
return Objects.equals(env, "prod");
}
}
I wrote a simple test, which set env's value to dev, and checked the result of isDev():
package com.example;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.util.ReflectionTestUtils;
#SpringBootTest
#ExtendWith(MockitoExtension.class)
class MainControllerTests {
#MockBean
private EnvUtils envUtils;
#Test
void fieldTest() {
ReflectionTestUtils.setField(envUtils, "env", "dev");
Assertions.assertTrue(envUtils.isDev());
}
}
The test should pass because the value of env is dev, which means envUtils.isDev() should definitely return true. However, this test failed. I debugged it and found that envUtils.env was dev, as expected, but envUtils.isDev() returned false. How could it be? Have I done something wrong?
You are mocking EnvUtils class, so the method isDev is mocked also, the real method of the class will not be invoked.
In your case, you don't need #MockBean, remove it. And don't forget to initialize the envUrils object using default constructor.
#SpringBootTest
#ExtendWith(MockitoExtension.class)
class MainControllerTests {
private EnvUtils envUtils = new EnvUtils();
#Test
void fieldTest() {
ReflectionTestUtils.setField(envUtils, "env", "dev");
Assertions.assertTrue(envUtils.isDev());
}
}
You mocked whole EnvUtils class.
If you change it to Spy it would be work.
Read this:mock vs spy
Try this:
#ExtendWith(MockitoExtension.class)
class MainControllerTests {
#SpyBean
private EnvUtils envUtils;
#Test
void fieldTest() {
ReflectionTestUtils.setField(envUtils, "env", "dev");
Assertions.assertTrue(envUtils.isDev());
}
}
We have an import problem with Eclipse :
the test class uses Assertions.assertThat
When hitting Ctrl + Shift + O to organize the imports, Eclipse replace Assertions.assertThat with StrictAssertions.assertThat
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
public class TheTest {
#Test
public void testName() {
assertThat(2).isEqualTo(2);
}
}
is replaced with :
import static org.assertj.core.api.StrictAssertions.assertThat; // change here !
import org.junit.Test;
public class TheTest {
#Test
public void testName() {
assertThat(2).isEqualTo(2);
}
}
And when we have some specific asserts that are only in Assertions (for lists), Eclipse add StrictAssertions to the imports.
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import org.junit.Test;
public class TheTest {
#Test
public void testName() {
assertThat(2).isEqualTo(2);
assertThat(new ArrayList<>()).isEmpty();
}
}
is changed to :
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.StrictAssertions.assertThat; // this import was added
import java.util.ArrayList;
import org.junit.Test;
public class TheTest {
#Test
public void testName() {
assertThat(2).isEqualTo(2);
assertThat(new ArrayList<>()).isEmpty();
}
}
It seems that Assertions extends StrictAssertions, so their is no problem using StrictAssertions, but why is Eclipse not using the extended class ?
Looks like, because assertThat(int actual) is defined in StrictAssertions and not hidden by Assertions, Eclipse decides to import from StrictAssertions.
Also, for organizing imports Eclipse seems to ignore Type Filters - so even that won't help.
It seems that Assertions extends StrictAssertions, so their is no problem using StrictAssertions
Not for your current setup, but StrictAssertions has been removed with AssertJ 3.2.0. So when upgrading to a newer version of AssertJ StrictAssertions will get in your way.
I'd suggest you upgrade to 3.2.0 or later, if it is possible with your project.
I have the following testSuite
package com.swaserver.junit;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import junit.framework.TestSuite;
public class MyTestSuite extends TestSuite
{
#RunWith(Suite.class)
#SuiteClasses( { BinarySearchTest.class })
public class RunTestSuite
{
}
}
However, It tells me I have no runnable methods. My Unit test itself runs fine in my IDE using Junit4 annotations so there is defo a valid JUnit test contained in it.
However the Test Suite above complains about no Runnable Methods when run using the IDE and ANT
package com.swaserver.junit;
import org.junit.Test;
public class BinarySearchTest
{
#Test
public void test()
{
}
}
I was referencing this online example
http://selftechy.com/2011/04/16/junit-4-executing-multiple-test-cases
MyTestSuite should have the annotations defined at the class level, like so:
#RunWith(Suite.class)
#SuiteClasses( { BinarySearchTest.class })
public class MyTestSuite
{
}
If you're using the annotation based approach you shouldn't extend TestSuite, also you don't need the constructor.
I have some simple classes I'm using to see if I can get powermock to work:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(Foo.class)
public class FooTest
{
#Test
public void testFoobar(){
Foo test = PowerMock.createPartialMock(Foo.class, "foobar");
PowerMock.replay(test);
}
}
and
public class Foo
{
public String foobar(String aString){
return aString + " blah";
}
}
When I try to run this unit test, it tells me:
java.lang.NoClassDefFoundError: org/easymock/classextension/internal/ClassProxyFactory$MockMethodInterceptor
...
I have no idea why its doing this. Please help.
Make sure you're including EasyMock in your class path when using PowerMock... you can find the download page here.
According to the Wiki on PowerMock, it states that EasyMock is a dependency.