I have an interface that defines a contract (i.e. a Repository), with few implementations. Each method in the interface represents a feature, and I would like to test each feature in its suite test class.
Let's assume a UserRepository interface as follows:
public interface UserRepository {
Set<User> search(String query);
Set<User> findBySomethingSpecific(String criteria1, Integer criteria2);
}
At the moment, to ensure I run the same test cases, I create an abstract test class, and each of my implementations have a test class that extends the abstract test class.
public abstract UserRepositoryTest {
private UserRepository userRepository;
#Before
public void setUp() {
userRepository = createUserRepository();
}
#Test public void aTestForSearch() { ... }
#Test public void anotherTestForSearch() { ... }
#Test public void aTestForSomethingSpecific() { ... }
#Test public void anotherTestForSomethingSpecific() { ... }
protected abstract UserRepository createUserRepository();
}
//------------------------
public class UserRepositoryImplementationTest extends UserRepositoryTest {
#Override
protected UserRepository createUserRepository() {
return new UserRepositoryImplementation();
}
}
I would like to find a way to divide this abstract test class into a set of small tests, because the test class becomes rapidly overwhelmed. I've looked at test suites, but I don't understand how can I create a Suite test class by injecting my different implementations.
As a side not, I've found this question, but some of my repositories require some logic at its creation (for instance, ConnectionPool for a SQL implementation). I currently use the anti-pattern ServiceLocator with different Context classes to handle the creation, but this is static. That's why I had an approach of a test class by implementation, so I can create the context and inject it afterward.
Whit Junit 4 you can create a suite like this:
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
#RunWith(Suite.class)
#Suite.SuiteClasses({
TestFeatureLogin.class,
TestFeatureLogout.class,
TestFeatureNavigate.class,
TestFeatureUpdate.class
})
/**
*
* This suite will execute TestFeatureLogin,TestFeatureLogout,TestFeatureNavigate and TestFeatureUpdate one after the over.
*
* #Before, #After and #Test are no possible of executing in this class.
* #BeforeClas and #AfterClass are allowed only.
*
* */
public class FeatureTestSuite {
// the class remains empty of test,although it is possible set up a before class and after class annotations.
// used only as a holder for the above annotations
#BeforeClass
static public void beforeClass(){
System.out.println(FeatureTestSuite.class.toString() + " BeforeClass Method");
}
#AfterClass
static public void AfterClass(){
System.out.println(FeatureTestSuite.class.toString() + " AfterClass Method");
}
}
The complete example could be found here
Another thing you have to have into account is that #Test is no a good practice of unit testing inside Abstract class. If you want to test your implementations create test classes that extend of Abstract class.
Related
I am working with:
Spring Framework 4.3.10
JUnit 4.12
Gradle 4.3.1
I have these two test classes
#Transactional
#RunWith(Parameterized.class)
#ContextConfiguration(classes={RootApplicationContext.class})
#ActiveProfiles(resolver=TestJdbcActiveProfilesResolver.class)
#TestExecutionListeners(listeners={LoggingTestExecutionListener.class}, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public class PersonaServiceImplJdbcTest {
#Transactional
#RunWith(Parameterized.class)
#ContextConfiguration(classes={RootApplicationContext.class})
#ActiveProfiles(resolver=TestHibernateActiveProfilesResolver.class)
#TestExecutionListeners(listeners={LoggingTestExecutionListener.class}, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public class PersonaServiceImplHibernateTest {
The code about the #Test methods are the same for both Test classes, breaking the DRY principle, the unique difference between these two test classes is the jdbc and Hibernate profiles working together with other such as development, mysql, it internally through each TestXXXActiveProfilesResolver class variation.
Until here I have 2 test classes, breaking the DRY principle, thinking in hierarchy I am going to get 3.
How (if is possible) use one Test class where for each interaction executes two (or more) sets of profiles such as:
jdbc,development,mysql
Hibernate,development,mysql
I already have read:
Spring Boot / JUnit, run all unit-tests for multiple profiles
But I want avoid use commands either through Maven or Gradle, it to keep the control through the TestXXXActiveProfilesResolver classes.
For JUnit 4 finally I did the following (scroll down):
#Transactional
#RunWith(Parameterized.class)
#ContextConfiguration(classes={RootApplicationContext.class})
//#ActiveProfiles() ... disable
#TestExecutionListeners(listeners={LoggingTestExecutionListener.class}, mergeMode=MergeMode.MERGE_WITH_DEFAULTS)
public abstract class PersonaServiceImplTest {
...
#Autowired
private Environment environment;
#Before
public void setup(){
logger.info("Profiles: {}", Arrays.toString(environment.getActiveProfiles()));
}
//#Test disable
public void someTest(){
assertThat(...)
}
#ActiveProfiles(resolver=TestJdbcActiveProfilesResolver.class)
public static class ForJdbc extends PersonaServiceImplTest {
public ForJdbc(Persona persona){
super(persona);
}
#Test
#Override
#Sql(scripts={"classpath:/.../script.sql"})//when be necessary
public void someTest()(){
super.someTest()();
}
}
#ActiveProfiles(resolver=TestHibernateActiveProfilesResolver.class)
public static class ForHibernate extends PersonaServiceImplTest {
public ForHibernate(Persona persona){
super(persona);
}
#Test
#Override
#Sql(scripts={"classpath:/.../script.sql"})//when be necessary
public void someTest()(){
super.someTest()();
}
...
}
}
Observations:
The outer class must be abstract
The outer class must have no #ActiveProfiles declared
The outer class has the methods to be tested, each one must have no the #Test declared
Environment is optional but is useful to let know the profiles activated for each static nested class, it through the common method annotated with #Before
Each static nested class must be public
Each static nested class must extends the outer class
Each static nested class must have #ActiveProfiles
Each static nested class overrides each test method, just to use super to call the respective overridden method
Each static nested class, for each test overridden method, it must have the #Test.
#Sql can't be reused, it must be declared for each overridden method
I have a particular class (let's say MyTest) in my Spring integration tests that is using PowerMock #PrepareForTest annotation on a Spring component: #PrepareForTest(MyComponent.class). This means that PowerMock will load this class with some modifications. The problem is, my #ContextConfiguration is defined on the superclass which is extended by MyTest, and the ApplicationContext is cached between different test classes. Now, if MyTest is run first, it will have the correct PowerMock version of MyComponent, but if not - the test will fail since the context will be loaded for another test (without #PrepareForTest).
So what I want to do is to reload my context before MyTest. I can do that via
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
But what if I also want to reload context after this test is done? So I will have clean MyComponent again without PowerMock modifications. Is there a way to do both BEFORE_CLASS and AFTER_CLASS?
For now I did it with the following hack:
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
on MyTest and then
/**
* Stub test to reload ApplicationContext before execution of real test methods of this class.
*/
#DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
#Test
public void aa() {
}
/**
* Stub test to reload ApplicationContext after execution of real test methods of this class.
*/
#DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
#Test
public void zz() {
}
I am wondering if there is a prettier way to do that?
As a side question, is it possible to reload only certain bean and not full context?
Is there a way to do both BEFORE_CLASS and AFTER_CLASS?
No, that is unfortunately not supported via #DirtiesContext.
However, what you're really saying is that you want a new ApplicationContext for MyTest that is identical to the context for the parent test class but only lives as long as MyTest. And... you don't want to affect the context cached for the parent test class.
So with that in mind, the following trick should do the job.
#RunWith(SpringJUnit4ClassRunner.class)
// Inherit config from parent and combine with local
// static Config class to create a new context
#ContextConfiguration
#DirtiesContext
public class MyTest extends BaseTests {
#Configuration
static class Config {
// No need to define any actual #Bean methods.
// We only need to add an additional #Configuration
// class so that we get a new ApplicationContext.
}
}
Alternative to #DirtiesContext
If you want to have a context dirtied both before and after a test class, you can implement a custom TestExecutionListener that does exactly that. For example, the following will do the trick.
import org.springframework.core.Ordered;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
public class DirtyContextBeforeAndAfterClassTestExecutionListener
extends AbstractTestExecutionListener {
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
}
#Override
public void afterTestClass(TestContext testContext) throws Exception {
testContext.markApplicationContextDirty(HierarchyMode.EXHAUSTIVE);
}
}
You can then use the custom listener in MyTest as follows.
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.TestExecutionListeners.MergeMode;
#TestExecutionListeners(
listeners = DirtyContextBeforeAndAfterClassTestExecutionListener.class,
mergeMode = MergeMode.MERGE_WITH_DEFAULTS
)
public class MyTest extends BaseTest { /* ... */ }
As a side question, is it possible to reload only certain bean and not full context?
No, that is also not possible.
Regards,
Sam (author of the Spring TestContext Framework)
I have following architecture of unit test:
#RunWith(Enclosed.class)
public class ProductTest {
#RunWith(MockitoJUnitRunner.class)
public static abstract class Base {...}
public static class Test1 extends Base{
#Test
public void foo(){...}
}
}
public static class Test2 extends Base{
#Test
public void bar(){...}
}
}
}
If I run unit tests I see following error message:
org.mockito.exceptions.base.MockitoException:
No tests found in Base
Haven't you forgot #Test annotation?
Mockito tries to say me that Base is test bit have noone method annoted with #Test
I have found decision - ignore base class.
Bit it looks like hack. Is there more elegant way?
Move Base outside the ProductTest class (can be in same file) and move RunWith to concrete classes.
Is it possible to have a common #Before and #After fixtures that can be used across multiple test classes?
I have segregated the tests (into classes) based on modules (Inventory, Sales, Purchase etc.). For all these tests, user Login is a prerequisite, currently I am having it in #Before for each class. The problem is when I need to change user id or password, I need to change in every class. Is there a way to write the #Before / #After that can be used in all the test classes? Does testsuite come handy by any means in this case?
The #Before and #After are applicable to inheritance:
public abstract class AbstractTestCase {
#Before
public void setUp() {
// do common stuff
}
}
If you want to do specific stuff in each test case you can override it:
public class ConcreteTestCase extends AbstractTestCase {
#Before
#Override
public void setUp() {
super.setUp();
// do specific stuff
}
}
You can create a #ClassRule for your test suite. It is invoked for each test. See API and Example for ExternalResource on how to apply before/after.
you can use #Before annotations in an abstract parent class like so:
public class TestMe extends TestParent {
#Test
public void test() {
System.out.println("Hi, here is a test. The username is: " + getUsername());
}
}
with parent class:
public abstract class TestParent {
private String username;
#Before
public void setUp() {
username = "fred";
}
public String getUsername() {
return username;
}
}
I know 2 solutions:
Use abstract base class as it is mentioned in comment by #vikingsteve.
Use Rules. You can implement your custom rule that does what you need and then add it to each test case you need.
public class MyTest {
#Rule Rule myBeforeAfterRule = new MyTestLifecycleRule();
// your code
}
The rule-based solution IMHO is more flexible because inheritance is not always applicable for all use-cases. Moreover you can combine several rules in one test case.
Is it possible to parameterize a TestSuite in junit 4 ?
For declaring a class as a test suite I need the annotation #RunWith(Suite.class), but the same annotation is also needed to declare the test as parameterized: #RunWith(Parameterized.class) so I cannot add both to the same class.
I found a similar question in this site that did not help much. So far, all the examples I have found explain how to parameterize simple unit tests, not a complete test tuite.
I believe the basic answer is No, because as you said, the #RunsWith only take one parameter. I found a blog posting that got a bit creative in how to handle this situation.
We don't use the parameterized tests, but may you could create a separate suite like we do that only lists the test classes and the parameterized test could be part of that. I modified our test suite to include a parameterized test class to part of the suite and it ran fine. We create our suite like below where PrimeNumberCheckerTest was a simple I pulled from the web.
package com.jda.portfolio.api.rest.server;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
#RunWith(Suite.class)
#SuiteClasses({ com.mycompany.api.rest.server.resource.TestCartResourceJava.class,
com.mycompany.api.rest.server.resource.TestCustomerResource.class,
com.mycompany.api.rest.server.resource.TestWizardProfileResource.class,
com.mycompany.api.rest.server.interceptor.TestBaseSearchInterceptor.class,
com.mycompany.api.rest.server.resource.TestQueryParameters.class,
com.mycompany.api.rest.server.expression.TestCartExpressionGenerator.class,
com.mycompany.api.rest.server.expression.TestPreferenceExpressionGenerator.class,
com.mycompany.api.rest.server.PrimeNumberCheckerTest.class,
})
public class AllTests {}
Here's the source for the parameterized test case;
package com.jda.portfolio.api.rest.server:
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Suite.SuiteClasses;
#RunWith(Parameterized.class)
#SuiteClasses({PrimeNumberCheckerTest.class})
public class PrimeNumberCheckerTest {
private Integer inputNumber;
private Boolean expectedResult;
private PrimeNumberChecker primeNumberChecker;
#Before
public void initialize() {
primeNumberChecker = new PrimeNumberChecker();
}
// Each parameter should be placed as an argument here
// Every time runner triggers, it will pass the arguments
// from parameters we defined in primeNumbers() method
public PrimeNumberCheckerTest(Integer inputNumber,
Boolean expectedResult) {
this.inputNumber = inputNumber;
this.expectedResult = expectedResult;
}
#Parameterized.Parameters
public static Collection primeNumbers() {
return Arrays.asList(new Object[][] {
{ 2, true },
{ 6, false },
{ 19, true },
{ 22, false },
{ 23, true }
});
}
// This test will run five times since we have as many parameters defined
#Test
public void testPrimeNumberChecker() {
System.out.println("Parameterized Number is : " + inputNumber);
assertEquals(expectedResult,
primeNumberChecker.validate(inputNumber));
}
I was able to parameterize a test suite and use its data in a test class member of the suite as follows:
In JUTsuite:
#RunWith(Suite.class)
#Suite.SuiteClasses({
JUT_test1.class,
})
public class JUTSuite{
// Declare all variables/objects you want to share with the test classes, e.g.
protected static List<Fx> globalFxs;
// This is the data list we'll use as parameters
protected static List<Dx> globalDxs;
#Parameters
public static Collection<Object[]> data(){
// Instantiate object list for parameters.
// Note: you must do it here and not in, say, #BeforeClass setup()
// e.g.
globalDxs=new ArrayList<Dx>(serverObj.values());
Collection<Object[]> rows=new ArrayList<Object[]>();
for(Dx d:globalDxs) {
rows.add(new Object[]{d});
}
return rows;
}
#BeforeClass
public static void setUp() throws Exception {
// Instantiate/initialize all suite variables/objects to be shares with test classes
// e.g. globalFxs=new ArrayList<Fx>();
}
#AfterClass
public static void tearDown() throws Exception {
// Clean up....
}
}
Next, in test class:
#RunWith(Parameterized.class)
public class JUT_test1 {
// declare local names (if desired) for suite-wide variable/objects
// e.g.
private static List<Fx> globalFxs;
// This is the test parameter:
private Dx d;
public JUT_test1(Dx d){
this.d=d;
}
#Parameters
public static Collection<Object[]> data(){
// Note: we're calling the suite's data() method which has already executed.
return JUTSuite.data();
}
#BeforeClass
public static void setUpBeforeClass() throws Exception {
// (If desired)initialize local variables by referencing suite variables.
// e.g.globalFxs=JUTSuite.globalFxs;
}
}
I agree, it's not possible with the provided classes, but there are workarounds that will get you most of the way there, like #mikemil's.
I've spent some time extending Suite and delegating to Parameterized, with partial success; it is possible to build runner that does what you want, and the code is more-or-less written for you in those two classes. The way those classes interact (in particular, the definition of Parameterized#getChildren()) makes it difficult to extend or delegate to those classes to accomplish what you need, but creating a whole new class than extends ParentRunner and lifts code from the other two would be fairly easy.
I'll try to get more time to come back to this later. If you do build a new runner before I get around to it, please post it as an answer, I'd love to use it myself.
the best solution will be, keep suit classes separately in a blank class.
For example, I am testing logins as Parameterized tests and putting in a suit (for navigation performance measurement)
#RunWith(Suite.class)
#Suite.SuiteClasses({
LoginPageTest.class,
HomePageTests.class})
public class PerformanceTests {
}
and LoginPageTest is actually Parameterizedtests
#RunWith(Parameterized.class)
public class LoginPageTest
{...}
As already stated multiple times, it's not possible to parameterize a test suite with the runners provided by JUnit 4.
Anyway, I wouldn't recommend to make your testclasses dependent from some externally provided state. What if you want to run a single testclass?
I would recommend to make your separate test classes #Parameterized and use a utility class to provide the parameters:
#RunWith(Suite.class)
#SuiteClasses({ Test1.class, Test2.class })
public class TestSuite {
// suite
}
#RunWith(Parameterized.class}
public class Test1 {
public Test1(Object param1) { /* ... */ }
#Parameters
public static Collection<Object[]> data() {
return TestParameters.provideTestData()
}
#Test
public void someTest() { /* ... */ }
}
#RunWith(Parameterized.class}
public class Test2 {
public Test2(Object param1) { /* ... */ }
#Parameters
public static Collection<Object[]> data() {
return TestParameters.provideTestData()
}
#Test
public void someOtherTest() { /* ... */ }
}
class TestParameters {
public static Collection<Object[]> provideTestData() {
Collection<Object[]> data = new ...;
// build testdata
return data;
}
You're right: Both Suite and Parameterized are Runners and only one Runner may be used to run a test at a time. Standard JUnit 4 doesn't provide a combined Runner.
You can either implement your own Runner or have a look at this ready-to-use library which provides a ParameterizedSuite Runner: https://github.com/PeterWippermann/parameterized-suite
A parameterized test suite looks like this:
#RunWith(ParameterizedSuite.class)
#SuiteClasses({OneTest.class, TwoTest.class})
public class MyParameterizedTestSuite {
#Parameters(name = "Parameters are {0} and {1}")
public static Object[] params() {
return new Object[][] {{'A',1}, {'B',2}, {'C',3}};
}
Maybe this answer helps: Parameterized unit test suites
It uses #RunWith(Enclosed.class) and seems to solve the problem.