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.
Related
I have such an example of test suite class to run many test classes at once in JUnit 4.13.
#RunWith(Suite.class)
#SuiteClasses({
FirstTest.class,
SecondTest.class
})
#TestSuiteAnnotation
public class TestSuite {
}
These are my test classes.
#FirstAnnotation
public class FirstTest extends ExtTest {
#Test
public void test() {
}
}
#SecondAnnotation
public class SecondTest extends ExtTest {
#Test
public void test() {
}
}
public class ExtTest {
#Before
public void beforeMethod() {
System.out.println("Annotations from " + this.getClass());
Arrays.asList(this.getClass().getAnnotations()).forEach(System.out::println);
}
}
When I run test from TestSuite.class, the console output is:
Annotations from class FirstTest
#FirstAnnotation()
Annotations from class SecondTest
#SecondAnnotation()
Currently, this.getClass().getAnnotations() returns annotations from test classes (i.e. FirstTest.class, SecondTest.class). I want to obtain annotation #TestSuiteAnnotation, when I run tests from TestSuite.class.
The expected output should be:
Annotations from class FirstTest
#FirstAnnotation()
#TestSuiteAnnotation()
Annotations from class SecondTest
#SecondAnnotation()
#TestSuiteAnnotation()
Can I somehow obtain annotation #TestSuiteAnnotation, when I run tests from TestSuite.class?
You have multiple options:
JUnit 4 run listener
On JUnit 4, you can register a RunListener, like #nrainer said. If you build with Maven, it is easy to register a run listener like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<includes>
<include>org.acme.TestSuite</include>
</includes>
<properties>
<property>
<name>listener</name>
<value>org.acme.SuiteRunListener</value>
</property>
</properties>
</configuration>
</plugin>
The run listener can override the events testSuiteStarted and testSuiteFinished and either directly log the annotations you are interested in or assign them to a static thread-local variable like private static ThreadLocal<List<Annotation>> currentSuiteAnnotations in testSuiteStarted, then unassign it again in testSuiteFinished.
This works nicely from Maven, I tested it. Unfortunately, there is no direct support for running tests with run listeners from IDEs like IntelliJ IDEA or Eclipse. So if you want to avoid running the tests manually from a class with a main method as shown here, because it would take away all the nice IDE test reporting with drill-down from suite to test class to test method, this is not an option.
JUnit 5 test execution listener
Similar to JUnit 4's run listener, you can register a TestExecutionListener for your JUnit 5 tests. The advantage in JUnit 5 is that you can register it globally via Java's s ServiceLoader mechanism, i.e. it will be picked up when bootstrapping JUnit and should also work in IDEs. I did something similar with another type of extension, and it worked nicely in IntelliJ IDEA and of course also in Maven.
JUnit 4 with custom suite runner
Coming back to JUnit 4, we can extend the first approach with the run listener by declaring a special type of suite. You simply use that suite instead of org.junit.runners.Suite and can enjoy the working run listener in both Maven and the IDE. It works like that, see also my MCVE on GitHub for your convenience:
package org.acme;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runner.notification.RunListener;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.stream.Collectors;
public class SuiteRunListener extends RunListener {
private static ThreadLocal<String> currentSuiteName = new ThreadLocal<String>();
private static ThreadLocal<List<Annotation>> currentSuiteAnnotations = new ThreadLocal<>();
#Override
public void testSuiteStarted(Description description) throws Exception {
super.testSuiteStarted(description);
final RunWith runWith = description.getAnnotation(RunWith.class);
if (runWith != null && runWith.value().equals(SuiteWithListener.class)) {
currentSuiteName.set(description.getDisplayName());
currentSuiteAnnotations.set(
description.getAnnotations().stream()
.filter(annotation -> {
final Class<? extends Annotation> annotationType = annotation.annotationType();
return !(annotationType.equals(RunWith.class) || annotationType.equals(SuiteClasses.class));
})
.collect(Collectors.toList())
);
}
}
#Override
public void testSuiteFinished(Description description) throws Exception {
super.testSuiteFinished(description);
final RunWith runWith = description.getAnnotation(RunWith.class);
if (runWith != null && runWith.value().equals(SuiteWithListener.class)) {
currentSuiteName.set(null);
currentSuiteAnnotations.set(null);
}
}
public static String getCurrentSuiteName() {
return currentSuiteName.get();
}
public static List<Annotation> getCurrentSuiteAnnotations() {
return currentSuiteAnnotations.get();
}
}
package org.acme;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.Suite;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;
import java.util.List;
public class SuiteWithListener extends Suite {
public SuiteWithListener(Class<?> klass, RunnerBuilder builder) throws InitializationError {
super(klass, builder);
}
public SuiteWithListener(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
super(builder, classes);
}
protected SuiteWithListener(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
super(klass, suiteClasses);
}
protected SuiteWithListener(RunnerBuilder builder, Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
super(builder, klass, suiteClasses);
}
protected SuiteWithListener(Class<?> klass, List<Runner> runners) throws InitializationError {
super(klass, runners);
}
#Override
public void run(RunNotifier notifier) {
notifier.addListener(new SuiteRunListener()); // !!!
super.run(notifier);
}
}
package org.acme;
import org.junit.runner.RunWith;
import org.junit.runners.Suite.SuiteClasses;
#RunWith(SuiteWithListener.class) // !!!
#SuiteClasses({
FirstTest.class,
SecondTest.class
})
#TestSuiteAnnotation
public class TestSuite {}
package org.acme;
import org.junit.Before;
import java.util.Arrays;
public class ExtTest {
#Before
public void beforeMethod() {
String currentSuiteName = SuiteRunListener.getCurrentSuiteName();
if (currentSuiteName != null) {
System.out.println("Annotations from suite " + currentSuiteName);
SuiteRunListener.getCurrentSuiteAnnotations().forEach(System.out::println);
}
System.out.println("Annotations from class " + this.getClass());
Arrays.asList(this.getClass().getAnnotations()).forEach(System.out::println);
System.out.println();
}
}
Now when running your suite, you should see output like this:
Annotations from suite org.acme.TestSuite
#org.acme.TestSuiteAnnotation()
Annotations from class class org.acme.FirstTest
#org.acme.FirstAnnotation()
Annotations from suite org.acme.TestSuite
#org.acme.TestSuiteAnnotation()
Annotations from class class org.acme.SecondTest
#org.acme.SecondAnnotation()
Please note: I was assuming that you really need access to the current suite from each single test method, not just at the test class or suite level. If you do not need that and it is enough to let the run listener do something when a suite is started and/or finished, of course you do not need the getter methods for current suite name and suite annotations. I just extended your own example.
I am using SpringJUnit4ClassRunner to run my Integration test case.
Now I am loading the data's to In memory HSQL for each junit test case in #Before method and Destroy it in #After method in Junit Test class.
I need to have data load should happen only once for all test case reside inside test folder . Also should be destroyed after completion of all test case.
I am using Maven for build,JDK8,Spring 4.2.5 and HSQL in memory.
Kindly help me to achieve this logic.
Yes it's possible using JUnit - Suite Test
Create your Test Suite including all the necessary test classes. Configure your setup and teardown methods here
package com.test;
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({Test1.class, Test2.class})
public class TestSuite {
#BeforeClass
public static void setUp() {
// Set up database
}
#AfterClass
public static void tearDown() {
// Cleanup codes
}
}
And create your test classes normally
package com.test;
import org.junit.Test;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context.xml" })
public class Test1 {
#Test
public void test1() {
System.out.println("test1");
}
}
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")
When I create an and run unit-test, (in Eclipse (Galileo) with JUnit 4.5 or 4.82),
the #Before is never executed (?).
Below is some sample-code. I would expect the output to be :
initialize
testGetFour
But it is just :
testGetFour
#BeforeClass and #AfterClass are never executed either.
Can someone tell me how come ?
public class SomeClass
{
public static int getFour()
{
return 4;
}
}
//---
import org.junit.Before;
import org.junit.Test;
import junit.framework.TestCase;
public class TestSomeClass extends TestCase
{
#Before
public void initialize() // This method will never execute (?!).
{
System.err.println("initialize");
}
#Test
public void testGetFour()
{
System.err.println("testGetFour");
assertEquals(4, SomeClass.getFour());
}
}
Because you're extending TestCase. This is a JUnit3 class, and so Eclipse treats it as such. JUnit 4 does not require the test class to extend anything.
Remove that, it should work fine.
You should not extend from TestCase (JUnit 3 way of using JUnit) and it will work.
I would like to create a junit test suite using JUnit 4 where the names of the test classes to be included are not known until the test suite is run.
In JUnit 3 I could do this:
public final class MasterTester extends TestCase
{
/**
* Used by junit to specify what TestCases to run.
*
* #return a suite containing what TestCases to run
*/
public static TestSuite suite() {
TestSuite suite = new TestSuite();
for(Class<?> klass : gatherTestClasses()) {
suite.addTestSuite(klass);
}
return suite;
}
}
and let the gatherTestClasses() method deal with figuring out what test classes to run.
In JUnit 4, the documentation says to use an annotation: #SuiteClasses({TestClass1.class, TestClass2.class...}) to build up my test suite. There are numerous SO answers showing how to do this. Unfortunately the examples I see do not seem to allow for passing a dynamically generated list of TestClasses.
This SO answer suggested I would have to subclass BlockJUnit4ClassRunner which I do not want to do.
Dynamically specified test suites seem like something that must be in JUnit 4 somewhere. Does anyone know where?
To create a dynamic test suite, you need to use the #RunWith annotation. There are two common ways to use it:
#RunWith(Suite.class)
This allows you to specify, which classes compose the test suite in question. This is equivalent to the JUnit 3 style:
import junit.framework.TestSuite;
import junit.framework.TestCase;
public final class MasterTester extends TestCase {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(TestClass1.class);
suite.addTestSuite(TestClass2.class);
// etc...
return suite;
}
}
The equivalent JUnit 4 class will be:
import org.junit.runners.Suite;
#RunWith(Suite.class)
#SuiteClasses({TestClass1.class, TestClass2.class})
public final class MasterTester {
}
#RunWith(AllTests.class)
This allows you to dynamically specify the tests, which compose the test suite. If your tests are not known until runtime, you cannot specify them in the annotations. You can use this construction instead. So, if the JUnit 3 code is:
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.framework.Test;
public final class MasterTester extends TestCase {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
for (Test test : findAllTestCasesRuntime()) {
suite.addTest(test);
}
return suite;
}
}
The equivalent JUnit 4 code will be:
import org.junit.runners.AllTests;
import junit.framework.TestSuite;
import junit.framework.Test;
#RunWith(AllTests.class)
public final class MasterTester {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
for (Test test : findAllTestCasesRuntime()) {
suite.addTest(test);
}
return suite;
}
}
I've tried this using JUnit 4.8 and it works:
#RunWith(AllTests.class)
public class SomeTests
{
public static TestSuite suite()
{
TestSuite suite = new TestSuite();
suite.addTest(new JUnit4TestAdapter(Test1.class));
suite.addTest(new JUnit4TestAdapter(Test2.class));
return suite;
}
}
I found Classpath suite quite useful when used with a naming convention on my test classes.
https://github.com/takari/takari-cpsuite
Here is an example:
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
#RunWith(ClasspathSuite.class)
#ClassnameFilters({".*UnitTest"})
public class MySuite {
}
I'm not sure what gatherTestClasses() does, but let's say it returns some tests when the OS is Linux and different tests when the OS is Windows. You can replicate that in JUnit 4.4 with assumptions:
#Test
public void onlyOnLinux() {
assumeThat(getOS(), is(OperatingSystem.LINUX));
// rest of test
}
#Test
public void onlyOnWindows() {
assumeThat(getOS(), is(OperatingSystem.WINDOWS));
// rest of test
}
#Test
public void anyOperatingSystem() {
// just don't call assumeThat(..)
}
The implementation of getOS() and OperatingSystem being your custom code.
Here is a Complete example how to implement that. it combines of two testCase classes and one suite.
ExampleInstrumentedTest:
import android.support.test.rule.ActivityTestRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
#RunWith(JUnit4.class)
public class ExampleInstrumentedTest {
#Rule
public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
#Test
public void checkInputs() throws Exception {
}
}
ExampleInstrumentedTest2:
import android.support.test.rule.ActivityTestRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
#RunWith(JUnit4.class)
public class ExampleInstrumentedTest2 {
#Rule
public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
#Test
public void checkInputs() throws Exception {
}
}
ExampleInstrumentedSuite:
import junit.framework.TestSuite;
import org.junit.runner.RunWith;
import org.junit.runners.AllTests;
#RunWith(AllTests.class)
public class ExampleInstrumentedSuite {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
suite.addTest(new junit.framework.JUnit4TestAdapter(ExampleInstrumentedTest.class));
suite.addTest(new junit.framework.JUnit4TestAdapter(ExampleInstrumentedTest2.class));
return suite;
}
}
Note that you should use #RunWith(JUnit4.class) instead of default #RunWith(AndroidJUnit4.class) in testCase Class
public class MyTestCase extends TestCase {
#Override
public void runTest() {
// define assertion here <===
assertEquals("yes", "yes");
}
}
#RunWith(AllTests.class)
public class DynamicTestSuite {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
// dynamically create your test case here <====
suite.addTest(new MyTestCase());
return suite;
}
}
Expanding on #kissLife's answer, here's a something you can paste and run that creates multiple tests on the fly:
import junit.framework.TestCase;
import junit.framework.TestSuite;
public final class TestJunit4DynamicConstruction {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
suite.addTest(new CompareInts(1, 1));
suite.addTest(new CompareInts(2, 2));
suite.addTest(new CompareInts(2, 1)); // huh, for some reason, 2 != 1
suite.addTest(new CompareInts(1, 1));
return suite;
}
static public class CompareInts extends TestCase {
private final int got;
private final int expected;
CompareInts(int got, int expected) {
super(Integer.toString(got) + ":" + Integer.toString(expected));
this.got = got;
this.expected = expected;
}
#Override
public void runTest() {
assertEquals(got, expected);
}
}
}
You'll run these tests:
TestJunit4DynamicConstruction$CompareInts.1:1
TestJunit4DynamicConstruction$CompareInts.2:2
TestJunit4DynamicConstruction$CompareInts.2:1
TestJunit4DynamicConstruction$CompareInts.1:1
TestJunit4DynamicConstruction$CompareInts
and get this error:
junit.framework.AssertionFailedError:
Expected :2
Actual :1
...
TestJunit4DynamicConstruction$CompareInts.runTest(TestJunit4DynamicConstruction.java:26)
...
Process finished with exit code 255