How to parameterize junit Test Suite - java

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.

Related

JUnit Contract Testing with Suites

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.

How to run PowerMock on dynamically created TestCase

I was trying to mock my test suites. My test framework creates test cases by scanning test files on disk. So each time the test cases are dynamically created.
I was trying to use PowerMock. Below is the thing I tried first.
public class GroupTestcase_T extends TestSuite {
static void run() {
scan();
junit.textui.TestRunner.run(g);
}
static void scan() {
// scan disk
for (MyTestCase t : tests) { addTest(t); }
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(ClassToStub.class)
public class MyTestCase extends TestCase {
public MyTestCase(TestInfo info) {...}
#Override
protected void setUp() throws Exception {
PowerMockito.mockStatic(ClassToStub.class);
when(ClassToStub.methodToStub())
.thenReturn(new FakeProxy());
}
#Test
public void test() throws Exception {
// Test!
}
}
Above code seems not working:
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
2. inside when() you don't call method on mock but on some other object.
3. the parent of the mocked class is not public.
It is a limitation of the mock engine.
I traced the code and found that PowerMockRunner are not called at all.
Also I tried manually force Junit to run it with PowerMockRunner:
Result result = junit.run(new PowerMockRunner(MyTestCase.class));
PowerMockRunner has only one constructor that takes the test class as parameter. My test cases are different each time but all share the same class.
Any idea how to use PowerMock if TestCase are dynamically created?
I was using Junit 4 / PowerMock 1.5
You can generate your tests with the parameterized tests feature and apply the #PowerMockRule.
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
#RunWith(Parameterized.class)
#PrepareForTest(ClassToStub.class)
public class MyTestCase{
#Parameters
public static Collection<Object[]> scan() {
return Arrays.asList(new Object[][] {
{ new TestInfo() }, { new TestInfo() } });
}
#Rule
public PowerMockRule rule = new PowerMockRule();
public MyTestCase(TestInfo info) {
// ...
}
#Test
public void test() throws Exception {
PowerMockito.mockStatic(ClassToStub.class);
PowerMockito.when(ClassToStub.methodToStub()).thenReturn(new FakeProxy());
assertTrue(ClassToStub.methodToStub() instanceof FakeProxy);
}
}
Beware, in your example, you are mixing junit 3 (extends TestSuite, protected setUp) and junit 4 (#Test) test definitions.

passing Parameterized input using Mockitos

I am using Mockito for unit testing. I am wondering if its possible to send Parametrized input parameters with as in Junit testing
e.g
#InjectMocks
MockClass mockClass = new MockClass();
#Test
public void mockTestMethod()
{
mockClass.testMethod(stringInput);
// here I want to pass a list of String inputs
// this is possible in Junit through Parameterized.class..
// wondering if its can be done in Mockito
}
In JUnit, Parameterized tests use a special runner that ensure that the test is instantiated multiple times, so each test method is called multiple times. Mockito is a tool for writing specific unit tests, so there is no built-in ability to run the same test multiple times with different Mockito expectations.
If you want your test conditions to change, your best bet is to do one of the following:
Parameterize your test using JUnit, with a parameter for the mock inputs you want;
Run a loop of different parameters in your test, which unfortunately avoids the "test one thing per method" philosophy
Extract a method that actually performs the test, and create a new #Test method for each mock you want.
Note that there's no prohibition on using mock objects as #Parameterized test parameters. If you're looking to parameterize based on mocks, you can do that, possibly creating the mock and setting the expectations in a static method on the test.
Note about runners: This Parameterized test runner conflicts with Mockito's MockitoJUnitRunner: Each test class can only have one runner. You'll want to switch to #Before and #After methods or a Mockito JUnit4 rule for your setup, if you use them both.
As an example, compressed from a different answer that explains more about Parameterized runners versus JUnit rules and lifting from the JUnit4 Parameterized Test doc page and MockitoRule doc page:
#RunWith(Parameterized.class)
public class YourComponentTest {
#Rule public MockitoRule rule = MockitoJUnit.rule();
#Mock YourDep mockYourDep;
#Parameters public static Collection<Object[]> data() { /* Return the values */ }
public YourComponentTest(Parameter parameter) { /* Save the parameter to a field */ }
#Test public void test() { /* Use the field value in assertions */ }
}
If you are stuck with an older version of mockito where MockitoRule isn't available, the other possibility is to initialize the mocks explicitely with MockitoAnnotations.initMocks:
#RunWith(Parameterized.class)
public class YourComponentTest {
#Mock YourDep mockYourDep;
#Parameter
public Parameter parameter;
#Parameters public static Collection<Object[]> data() { /* Return the values */ }
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test public void test() { /* Use the field value in assertions */ }
}
You can use the JUnitParamsRunner. Here's how I do it:
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.Arrays;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
#RunWith(value = JUnitParamsRunner.class)
public class ParameterizedMockitoTest {
#InjectMocks
private SomeService someService;
#Mock
private SomeOtherService someOtherService;
#Before
public void setup() {
initMocks(this);
}
#Test
#Parameters(method = "getParameters")
public void testWithParameters(Boolean parameter, Boolean expected) throws Exception {
when(someOtherService.getSomething()).thenReturn(new Something());
Boolean testObject = someService.getTestObject(parameter);
assertThat(testObject, is(expected));
}
#Test
public void testSomeBasicStuffWithoutParameters() {
int i = 0;
assertThat(i, is(0));
}
public Iterable getParameters() {
return Arrays.asList(new Object[][]{
{Boolean.TRUE, Boolean.TRUE},
{Boolean.FALSE, Boolean.FALSE},
});
}
}
What solved it for me was:
Class level annotation of #ExtendWith(MockitoExtension.class)
Annotate each mock object with #Mock
#InjectMocks on the test class. Or a setup method annotated with #BeforeEach where you initialise the class to be tested.
if you need the #test annotation, make sure you import org.junit.jupiter.api.Test. org.junit.test will not work!
I'm using mockito version 4.

Setting Test Suite to Ignore

I have many Test Suites with each one contains many Test Classes. Here is how they look like:
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
#RunWith(Suite.class)
#SuiteClasses( {ATest.class, BTest.class})
public class MyFirstTestSuite {
#BeforeClass
public static void beforeClass() throws Exception {
// load resources
}
#AfterClass
public static void afterClass() throws Exception {
// release resources
}
}
Sometimes I want to disable a whole Test Suite completely. I don't want to set each test class as #Ignore, since every test suite loads and releases resources using #BeforeClass and #AfterClass and I want to skip this loading/releasing when the test suite is ignored.
So the question is: is there anything similar to #Ignore that I can use on a whole Test Suite?
You can annotate the TestSuite with #Ignore.
#RunWith(Suite.class)
#SuiteClasses({Test1.class})
#Ignore
public class MySuite {
public MySuite() {
System.out.println("Hello world");
}
#BeforeClass
public static void hello() {
System.out.println("beforeClass");
}
}
doesn't produce any output.
SlowTest is a class defined by user. It can be empty (without any functions or attributes). You can name it whatever you want:

Test cases in inner classes with JUnit

I read about Structuring Unit Tests with having a test class per class and an inner class per method. Figured that seemed like a handy way to organize the tests, so I tried it in our Java project. However, the tests in the inner classes doesn't seem to be picked up at all.
I did it roughly like this:
public class DogTests
{
public class BarkTests
{
#Test
public void quietBark_IsAtLeastAudible() { }
#Test
public void loudBark_ScaresAveragePerson() { }
}
public class EatTests
{
#Test
public void normalFood_IsEaten() { }
#Test
public void badFood_ThrowsFit() { }
}
}
Does JUnit not support this, or am I just doing it wrong?
You should annontate your class with #RunWith(Enclosed.class), and like others said, declare the inner classes as static:
#RunWith(Enclosed.class)
public class DogTests
{
public static class BarkTests
{
#Test
public void quietBark_IsAtLeastAudible() { }
#Test
public void loudBark_ScaresAveragePerson() { }
}
public static class EatTests
{
#Test
public void normalFood_IsEaten() { }
#Test
public void badFood_ThrowsFit() { }
}
}
public class ServicesTest extends TestBase {
public static class TestLogon{
#Test
public void testLogonRequest() throws Exception {
//My Test Code
}
}
}
Making the inner class static works for me.
In JUnit 5, you simply mark non-static inner classes as #Nested:
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
public class DogTests {
#Nested
public class BarkTests {
#Test
public void quietBark_IsAtLeastAudible() { }
#Test
public void loudBark_ScaresAveragePerson() { }
}
#Nested
public class EatTests {
#Test
public void normalFood_IsEaten() { }
#Test
public void badFood_ThrowsFit() { }
}
}
I think some of the answers might be for older versions of JUnit. In JUnit 4 this worked for me:
#RunWith(Suite.class)
#Suite.SuiteClasses({ DogTests.BarkTests.class, DogTests.EatTests.class })
public class DogTests
{
public static class BarkTests
{
#Test
public void quietBark_IsAtLeastAudible() { }
#Test
public void loudBark_ScaresAveragePerson() { }
}
public static class EatTests
{
#Test
public void normalFood_IsEaten() { }
#Test
public void badFood_ThrowsFit() { }
}
}
I've had success with Nitor Creation's Nested Runner as well.
How to use Nitor Creation's Nested Runner
There is a post explaining it here:
Add this dependency:
<dependency>
<groupId>com.nitorcreations</groupId>
<artifactId>junit-runners</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
And a #RunWith to your test:
import com.nitorcreations.junit.runners.NestedRunner
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
#RunWith(NestedRunner.class)
public class RepositoryUserServiceTest {
public class RegisterNewUserAccount {
public class WhenUserUsesSocialSignIn {
public class WhenUserAccountIsFoundWithEmailAddress {
#Test
public void shouldThrowException() {
assertTrue(true);
}
}
}
}
}
PS: The example code has been taken and modified from the above blog post
I just ran across this posting (11 years later) regarding the testing of inner classes. An inner class can be trivially converted to equivalent static form only if the class should have been static in the first place. Static inner classes are not really inner classes because there is no enclosing this. They have exactly the same semantics (except for visibility restrictions) as top-level classes.
To test a "true" inner class [one that depends on its enclosing instance] you need to use the interface that the Java language provides for creating inner class instances outside the scope of the enclosing class. That interface includes an extra parameter in each constructor which is the enclosing instance. In this way, the Java compiler converts an inner class to a special top-level class with a mangled name (lots of $ signs) and augmented constructors. The same transformation can be performed at the source level. In principle, these transformed classes can be tested but it is a complex process because the tested program has transformed syntax and the test code must construct a (mock) object that serves as the enclosing instance.
Another way to test true inner classes is to write an executable method contract for each method consisting of an executable logical pre-condition and an executable logical post-condition. Then these executable contracts can be evaluated in the course of running a conventional top-level test that invokes the inner class methods.
In practice, I typically settle for the indirect testing of inner class methods in the course of top-level testing. Writing and testing executable contracts for all methods is a more rigorous, albeit significantly more expensive, alternative.

Categories