JUnit: how to avoid "no runnable methods" in test utils classes - java

I have switched to JUnit4.4 from JUnit3.8. I run my tests using ant, all my tests run successfully but test utility classes fail with "No runnable methods" error. The pattern I am using is to include all classes with name *Test* under test folder.
I understand that the runner can't find any method annotated with #Test attribute. But they don't contain such annotation because these classes are not tests.
Surprisingly when running these tests in eclipse, it doesn't complain about these classes.
In JUnit3.8 it wasn't a problem at all since these utility classes didn't extend TestCase so the runner didn't try to execute them.
I know I can exclude these specific classes in the junit target in ant script. But I don't want to change the build file upon every new utility class I add. I can also rename the classes (but giving good names to classes was always my weakest talent :-) )
Is there any elegant solution for this problem?

Annotate your util classes with #Ignore. This will cause JUnit not to try and run them as tests.

My specific case has the following scenario. Our tests
public class VenueResourceContainerTest extends BaseTixContainerTest
all extend
BaseTixContainerTest
and JUnit was trying to run BaseTixContainerTest. Poor BaseTixContainerTest was just trying to setup the container, setup the client, order some pizza and relax... man.
As mentioned previously, you can annotate the class with
#Ignore
But that caused JUnit to report that test as skipped (as opposed to completely ignored).
Tests run: 4, Failures: 0, Errors: 0, Skipped: 1
That kind of irritated me.
So I made BaseTixContainerTest abstract, and now JUnit truly ignores it.
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

Assuming you're in control of the pattern used to find test classes, I'd suggest changing it to match *Test rather than *Test*. That way TestHelper won't get matched, but FooTest will.

To prevent JUnit from instantiating your test base class just make it
public abstract class MyTestBaseClass { ... whatever... }
(#Ignore reports it as ignored which I reserve for temporarily ignored tests.)

If this is your base test class for example AbstractTest and all your tests extends this then define this class as abstract
If it is Util class then better remove *Test from the class rename it is MyTestUtil or Utils etc.

Be careful when using an IDE's code-completion to add the import for #Test.
It has to be import org.junit.Test and not import org.testng.annotations.Test, for example. If you do the latter, you'll get the "no runnable methods" error.

Ant now comes with the skipNonTests attribute which was designed to do exactly what you seem to be looking for. No need to change your base classes to abstract or add annotations to them.

What about adding an empty test method to these classes?
public void avoidAnnoyingErrorMessageWhenRunningTestsInAnt() {
assertTrue(true); // do nothing;
}

In your test class if wrote import org.junit.jupiter.api.Test; delete it and write import org.junit.Test; In this case it worked me as well.

I was also facing a similar issue ("no runnable methods..") on running the simplest of simple piece of code (Using #Test, #Before etc.) and found the solution nowhere. I was using Junit4 and Eclipse SDK version 4.1.2. Resolved my problem by using the latest Eclipse SDK 4.2.2. I hope this helps people who are struggling with a somewhat similar issue.

I also faced the same issue once. In my case I was running my tests using Enclosed Runner of Junit. I created a class called SharedSetup to enable common features for my 2 test classes. But somehow facing the same issue.
#RunWith(Enclosed.class)
public class StructApprovalNodeTest {
abstract static class SharedSetup {
StructApprovalNode sut;
ExecutionContext ctx = mock(ExecutionContext.class);
DTDDAOService dtd = mock(DTDDAOService.class);
#Rule
public ExpectedException expectedException = ExpectedException.none();
#Before
public void before() throws Exception {
PowerMockito.mockStatic(ServiceHelper.class);
when(ServiceHelper.getService("dtd")).thenReturn(dtd);
when(ctx.getContextInstance()).thenReturn(mock(ContextInstance.class));
when(dtd.getLatestStructures(Matchers.anyInt(), Matchers.anyString(), Matchers.anyString())).thenReturn(
new ArrayList<Trade>());
sut = new StructApprovalNode();
spy(sut);
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ServiceHelper.class, StructApprovalNode.class })
#PowerMockIgnore("javax.management.*")
#PowerMockRunnerDelegate(Parameterized.class)
public static class ParamaterizedBatchTest extends SharedSetup {
private String batchName;
private String approvalStatus;
public ParamaterizedBatchTest(String batchName, String approvalStatus) {
this.batchName = batchName;
this.approvalStatus = approvalStatus;
}
#Parameterized.Parameters
public static Collection testValues() {
return Arrays.asList(new Object[][] {
{ "SDC_HK_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED },
{ "SDC_PB_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED },
{ "SDC_FX_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED }
});
}
#Test
public void test1_SDCBatchSourceSystems() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem(this.batchName);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(null);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(this.approvalStatus, trade.getApprovalStatus());
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ServiceHelper.class, StructApprovalNode.class })
#PowerMockIgnore("javax.management.*")
public static class NonParamaterizedBatchTest extends SharedSetup {
#Test
public void test2_PrevInvalidTrade() throws Exception {
expectedException.expect(Exception.class);
expectedException.expectMessage("External Id of STRUCTURE_TRADE cannot be changed.");
Trade trade = new Trade();
trade.setExternalId(123);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1234);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
}
#Test
public void test3_ValidPrevTrade() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1277402441);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals("APPROVED", trade.getApprovalStatus());
}
#Test
public void test4_ValidPrevTradeAutoApprpve() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML_AUTO_APPRV.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1277402441);
prevTrade.setApprovalStatus(Constants.APRVLSTATUS_NOTAPPROVED);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(prevTrade.getApprovalStatus(), trade.getApprovalStatus());
}
#Test
public void test5_tradeStatusDraft() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_DRAFT);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(null);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(Constants.APRVLSTATUS_NONE, trade.getApprovalStatus());
}
}
}
To solve the issue, I just removed the public modifier from the abstract super class SharedSetup and issue is fixed for good

Related

Mockito mocks locally final class but fails in Jenkins

I have written some unit tests for a static method. The static method takes only one argument. The argument's type is a final class. In terms of code:
public class Utility {
public static Optional<String> getName(Customer customer) {
// method's body.
}
}
public final class Customer {
// class definition
}
So for the Utility class I have created a test class UtilityTests in which I have written tests for this method, getName. The unit testing framework is TestNG and the mocking library that is used is Mockito. So a typical test has the following structure:
public class UtilityTests {
#Test
public void getNameTest() {
// Arrange
Customer customerMock = Mockito.mock(Customer.class);
Mockito.when(...).thenReturn(...);
// Act
Optional<String> name = Utility.getName(customerMock);
// Assert
Assert.assertTrue(...);
}
}
What is the problem ?
Whereas the tests run successfully locally, inside IntelliJ, they fail on Jenkins (when I push my code in the remote branch, a build is triggered and unit tests run at the end). The error message is sth like the following:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
com.packagename.Customer Mockito
cannot mock/spy because :
- final class
What I tried ?
I searched a bit, in order to find a solution but I didn't make it. I note here that I am not allowed to change the fact that Customer is a final class. In addition to this, I would like if possible to not change it's design at all (e.g. creating an interface, that would hold the methods that I want to mock and state that the Customer class implements that interface, as correctly Jose pointed out in his comment). The thing that I tried is the second option mentioned at mockito-final. Despite the fact that this fixed the problem, it brake some other unit tests :(, that cannot be fixed in none apparent way.
Questions
So here are the two questions I have:
How that is possible in the first place ? Shouldn't the test fail both locally and in Jenkins ?
How this can be fixed based in the constraints I mentioned above ?
Thanks in advance for any help.
An alternative approach would be to use the 'method to class' pattern.
Move the methods out of the customer class into another class/classes, say CustomerSomething eg/CustomerFinances (or whatever it's responsibility is).
Add a constructor to Customer.
Now you don't need to mock Customer, just the CustomerSomething class! You may not need to mock that either if it has no external dependencies.
Here's a good blog on the topic: https://simpleprogrammer.com/back-to-basics-mock-eliminating-patterns/
How that is possible in the first place? Shouldn't the test fail both locally and in Jenkins ?
It's obviously a kind of env-specifics. The only question is - how to determine the cause of difference.
I'd suggest you to check org.mockito.internal.util.MockUtil#typeMockabilityOf method and compare, what mockMaker is actually used in both environments and why.
If mockMaker is the same - compare loaded classes IDE-Client vs Jenkins-Client - do they have any difference on the time of test execution.
How this can be fixed based in the constraints I mentioned above?
The following code is written in assumption of OpenJDK 12 and Mockito 2.28.2, but I believe you can adjust it to any actually used version.
public class UtilityTest {
#Rule
public InlineMocksRule inlineMocksRule = new InlineMocksRule();
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Test
public void testFinalClass() {
// Given
String testName = "Ainz Ooal Gown";
Client client = Mockito.mock(Client.class);
Mockito.when(client.getName()).thenReturn(testName);
// When
String name = Utility.getName(client).orElseThrow();
// Then
assertEquals(testName, name);
}
static final class Client {
final String getName() {
return "text";
}
}
static final class Utility {
static Optional<String> getName(Client client) {
return Optional.ofNullable(client).map(Client::getName);
}
}
}
With a separate rule for inline mocks:
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.util.MockUtil;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class InlineMocksRule implements TestRule {
private static Field MOCK_MAKER_FIELD;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
VarHandle modifiers = lookup.findVarHandle(Field.class, "modifiers", int.class);
MOCK_MAKER_FIELD = MockUtil.class.getDeclaredField("mockMaker");
MOCK_MAKER_FIELD.setAccessible(true);
int mods = MOCK_MAKER_FIELD.getModifiers();
if (Modifier.isFinal(mods)) {
modifiers.set(MOCK_MAKER_FIELD, mods & ~Modifier.FINAL);
}
} catch (IllegalAccessException | NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Object oldMaker = MOCK_MAKER_FIELD.get(null);
MOCK_MAKER_FIELD.set(null, Plugins.getPlugins().getInlineMockMaker());
try {
base.evaluate();
} finally {
MOCK_MAKER_FIELD.set(null, oldMaker);
}
}
};
}
}
Make sure you run the test with the same arguments. Check if your intellij run configurations match the jenkins. https://www.jetbrains.com/help/idea/creating-and-editing-run-debug-configurations.html. You can try to run test on local machine with the same arguments as on jenkins(from terminal), if it will fail that means the problem is in arguments

SpringRunner/JUnit running/testing a private (non-test) method

We have a Spring 5 application using JUnit 4 as our test harness (w/ SpringRunner). We're experiencing an issue where a private helper method that's not marked with a #Test annotation is being run as a test. This happens in both IntelliJ and Maven.
The method signature is:
private Optional<TestSuiteJsonObject> createTestSuite(String name, TestType type) throws IOException, LicenseException {
And the test class itself looks like:
public class TestSuitesControllerTest extends ControllerTest
There are no annotations on either. The ControllerTest looks like:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = CompanyTestApplication.class)
#AutoConfigureMockMvc
#Ignore
public class ControllerTest {
...
}
The behavior is that these test methods are run with null arguments. We don't want them to be run at all. The rest of the methods in this class are appropriately marked with the #Test annotation.
I wondered if the fact that the word test is in the class/method name could be causing JUnit to identify it as runnable, but changing the names of both the class and method has no effect.
Adding #Ignore to the private method also has no effect!
What are we doing wrong? Did we step into an alternate dimension where test harnesses are actually testing stress responses of the engineers?
It was a silly mistake, but I'm leaving it for any future folk who find themselves in the same situation.
I had two methods with the same name. The test method:
#Test
#WithMockUser(username = "admin", roles = "ADMIN")
public void createSuite() throws Exception { ... }
And the helper method:
private static Optional<TestSuiteJsonObject> createSuite(String name, TestType type) { ... }
And we somehow glossed over this. 🤦‍♂️

Adding the class for which Junit is written in #PrepareForTest makes a Zero coverage in sonar

Sample Controller
public class SampleController {
public void sampleMethod() {
ClassAbc classAbc = new ClassAbc();
classAbc.abcMethod();
//doStuff
}
}
ClassAbc
public class ClassAbc {
public void abcMethod() {
//doStuff
}
}
Junit for SampleController
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#PrepareForTest({SampleController.class})
public class SampleControllerTest {
#Autowired
SampleController sampleController;
public void setUp() {
ClassAbc classAbc = PowerMockito.mock(ClassAbc.class);
PowerMockito.whenNew(ClassAbc.class).withAnyArguments()
.thenReturn(classAbc);
doNothing().when(classAbc).abcMethod();
}
#Test
public void testsampleMethod() throws Exception {
sampleController.sampleMethod();
}
}
When Iam adding the "SampleController.class" in #PrepareForTest ie:
#PrepareForTest({SampleController.class})
Iam getting the following violation when I run Sonar job.
14 more branches need to be covered by unit tests to reach the minimum threshold of 65.0% branch coverage.
That is Iam getting zero coverage for that class.
I would like to like if it is necessary to put the class where the constructor is called into the #PrepareForTest annotation instead of the class which is being constructed.
Or can someone find me a solution to fix that Sonar violation.
Thanks in advance
As stated in PowerMockito wiki:
... right now there is NO WAY TO USE PowerMock with JaCoCo On-the-fly instrumentation.
... to get code coverage with JaCoCo - use offline Instrumentation ...
You may find example of using PowerMock with JaCoCo Offline Instrumentation and Maven in our repository: jacoco-offline example.

Mocking a class object using Mockito and PowerMockito

Is it possible to mock a class object using Mockito and/or PowerMockito?
Something like:
Class<Runnable> mockRunnableClass = mock(Class<Runnable>.class);
An alternative to mocking Class might be to use a Factory instead. I know you are concerned about refactoring, but this could be done without changing the public API of the class. You haven't provided much code to understand the class you are trying to test, but here's an example of refactoring without changing the API. It's a trivial class, but it might give you an idea.
public class Instantiator {
public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
return runnableClass.newInstance();
}
}
Of course, the easiest thing to do to test this trivial class would be to use a genuine Runnable class, but if you tried to mock the Class, you would run into the problems you're having. So, you could refactor it thus:
public class PassThruFactory {
public Object newInstance(Class<?> clazz) throws Exception {
return clazz.newInstance();
}
}
public class Instantiator {
private PassThruFactory factory = new PassThruFactory();
public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
return (Runnable)factory.newInstance(runnableClass);
}
}
Now Instantiator does exactly the (trivially simple) thing it was doing before with the same public API and no need for any client of the class to do any special injecting of their own. However, if you wanted to mock the factory class and inject it, that's very easy to do.
why not using an agent if you can't refactor the code there isn't many options, as #jherics mentionned, java system classes are loaded by the bootstrap classloader and powermock can't redefine their bytecode.
However Powermock now coms with an agent, that will allow system classes mock. Check here for complete explanation.
The main idea is to modify your java command and add :
-javaagent: path/to/powermock-module-javaagent-1.4.12.jar
The basic thing this agent is doing is to definalize classes, to allow future mocking in a specific test, that's why you'll need to use specific types to communicate with the agent, for example with JUnit :
#Rule PowerMockRule rule = new PowerMockRule(); // found in the junit4 rule agent jar
TestNG is also supported. Just check the wiki page for more information.
Hope that helps.
First, as stated in the comments, you would need to do:
Class<Runnable> mockRunnableaClass = (Class<Runnable>)mock(Class.class);
But that won't work in the usual way because of a limitation with PowerMock. You cannot simply mock classes in from java.lang, java.net, java.io or other system classes because they're loaded by Java's bootstrap classloader and cannot be byte-code manipulated by PowerMock's classloader. (See PowerMock FAQ #4.) As of PowerMock 1.2.5, you can work around this. If the class you wanted to test was this:
public class ClassToTest {
private Class<Runnable> runnableClass;
public void setRunnableClass(Class<Runnable> runnableClass) {
this.runnableClass = runnableClass;
}
public Runnable foo() {
return runnableClass.newInstance();
}
}
Then you would do this:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ClassToTest.class }) // Prepare the calling class for test
public class SystemClassUserTest {
#Test
public void testFoo() throws Exception {
Class<Runnable> mockClass = (Class<Runnable>) mock(Class.class);
Runnable mockRunnable = mock(Runnable.class);
ClassToTest objectUT = new ClassToTest();
objectUT.setRunnableClass(mockClass);
when(mockClass.newInstance()).thenReturn(mockRunnable);
assertThat(objectUT.foo(), is(sameInstance(mockRunnable);
}
}
How about this. creating a get method of the has a Object (MS) in class PCService and then mock it.
public class PCService implements PCServiceIf {
public MSIf getMS() {
return ms;
}
private MSIf ms = new MS();
public boolean isMovieAccessibleToMyLevel(String myLevel, String movieId) {
return getMS().getPCL(movieId);
}
}
#Test
public void testIsMovieAccessibleToMyLevelMock() {
msMock = mock(MS.class);
spy = spy(new PCService());
doReturn(msMock).when(spy).getMS();
when(msMock.getPCL(movieId)).thenReturn(value);
when(spy.getMS().getPCL(movieId)).thenReturn(value);
assertTrue(spy.isMovieAccessibleToMyLevel("PG", movieId) == true);
}

Before and After Suite execution hook in jUnit 4.x

I'm trying to preform setup and teardown for a set of integration tests, using jUnit 4.4 to execute the tests. The teardown needs to be run reliably. I'm having other problems with TestNG, so I'm looking to port back to jUnit. What hooks are available for execution before any tests are run and after all tests have completed?
Note: we're using maven 2 for our build. I've tried using maven's pre- & post-integration-test phases, but, if a test fails, maven stops and doesn't run post-integration-test, which is no help.
Yes, it is possible to reliably run set up and tear down methods before and after any tests in a test suite. Let me demonstrate in code:
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() {
System.out.println("setting up");
}
#AfterClass
public static void tearDown() {
System.out.println("tearing down");
}
}
So your Test1 class would look something like:
package com.test;
import org.junit.Test;
public class Test1 {
#Test
public void test1() {
System.out.println("test1");
}
}
...and you can imagine that Test2 looks similar. If you ran TestSuite, you would get:
setting up
test1
test2
tearing down
So you can see that the set up/tear down only run before and after all tests, respectively.
The catch: this only works if you're running the test suite, and not running Test1 and Test2 as individual JUnit tests. You mentioned you're using maven, and the maven surefire plugin likes to run tests individually, and not part of a suite. In this case, I would recommend creating a superclass that each test class extends. The superclass then contains the annotated #BeforeClass and #AfterClass methods. Although not quite as clean as the above method, I think it will work for you.
As for the problem with failed tests, you can set maven.test.error.ignore so that the build continues on failed tests. This is not recommended as a continuing practice, but it should get you functioning until all of your tests pass. For more detail, see the maven surefire documentation.
A colleague of mine suggested the following: you can use a custom RunListener and implement the testRunFinished() method: http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html#testRunFinished(org.junit.runner.Result)
To register the RunListener just configure the surefire plugin as follows:
http://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html section "Using custom listeners and reporters"
This configuration should also be picked by the failsafe plugin.
This solution is great because you don't have to specify Suites, lookup test classes or any of this stuff - it lets Maven to do its magic, waiting for all tests to finish.
You can use the #ClassRule annotation in JUnit 4.9+ as I described in an answer another question.
Using annotations, you can do something like this:
import org.junit.*;
import static org.junit.Assert.*;
import java.util.*;
class SomethingUnitTest {
#BeforeClass
public static void runBeforeClass()
{
}
#AfterClass
public static void runAfterClass()
{
}
#Before
public void setUp()
{
}
#After
public void tearDown()
{
}
#Test
public void testSomethingOrOther()
{
}
}
Here, we
upgraded to JUnit 4.5,
wrote annotations to tag each test class or method which needed a working service,
wrote handlers for each annotation which contained static methods to implement the setup and teardown of the service,
extended the usual Runner to locate the annotations on tests, adding the static handler methods into the test execution chain at the appropriate points.
As for "Note: we're using maven 2 for our build. I've tried using maven's pre- & post-integration-test phases, but, if a test fails, maven stops and doesn't run post-integration-test, which is no help."
you can try the failsafe-plugin instead, I think it has the facility to ensure cleanup occurs regardless of setup or intermediate stage status
Provided that all your tests may extend a "technical" class and are in the same package, you can do a little trick :
public class AbstractTest {
private static int nbTests = listClassesIn(<package>).size();
private static int curTest = 0;
#BeforeClass
public static void incCurTest() { curTest++; }
#AfterClass
public static void closeTestSuite() {
if (curTest == nbTests) { /*cleaning*/ }
}
}
public class Test1 extends AbstractTest {
#Test
public void check() {}
}
public class Test2 extends AbstractTest {
#Test
public void check() {}
}
Be aware that this solution has a lot of drawbacks :
must execute all tests of the package
must subclass a "techincal" class
you can not use #BeforeClass and #AfterClass inside subclasses
if you execute only one test in the package, cleaning is not done
...
For information: listClassesIn() => How do you find all subclasses of a given class in Java?
As far as I know there is no mechanism for doing this in JUnit, however you could try subclassing Suite and overriding the run() method with a version that does provide hooks.
Since maven-surefire-plugin does not run Suite class first but treats suite and test classes same, so we can configure plugin as below to enable only suite classes and disable all the tests. Suite will run all the tests.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<includes>
<include>**/*Suite.java</include>
</includes>
<excludes>
<exclude>**/*Test.java</exclude>
<exclude>**/*Tests.java</exclude>
</excludes>
</configuration>
</plugin>
The only way I think then to get the functionality you want would be to do something like
import junit.framework.Test;
import junit.framework.TestResult;
import junit.framework.TestSuite;
public class AllTests {
public static Test suite() {
TestSuite suite = new TestSuite("TestEverything");
//$JUnit-BEGIN$
suite.addTestSuite(TestOne.class);
suite.addTestSuite(TestTwo.class);
suite.addTestSuite(TestThree.class);
//$JUnit-END$
}
public static void main(String[] args)
{
AllTests test = new AllTests();
Test testCase = test.suite();
TestResult result = new TestResult();
setUp();
testCase.run(result);
tearDown();
}
public void setUp() {}
public void tearDown() {}
}
I use something like this in eclipse, so I'm not sure how portable it is outside of that environment
If you don't want to create a suite and have to list all your test classes you can use reflection to find the number of test classes dynamically and count down in a base class #AfterClass to do the tearDown only once:
public class BaseTestClass
{
private static int testClassToRun = 0;
// Counting the classes to run so that we can do the tear down only once
static {
try {
Field field = ClassLoader.class.getDeclaredField("classes");
field.setAccessible(true);
#SuppressWarnings({ "unchecked", "rawtypes" })
Vector<Class> classes = (Vector<Class>) field.get(BlockJUnit4ClassRunner.class.getClassLoader());
for (Class<?> clazz : classes) {
if (clazz.getName().endsWith("Test")) {
testClassToRun++;
}
}
} catch (Exception ignore) {
}
}
// Setup that needs to be done only once
static {
// one time set up
}
#AfterClass
public static void baseTearDown() throws Exception
{
if (--testClassToRun == 0) {
// one time clean up
}
}
}
If you prefer to use #BeforeClass instead of the static blocks, you can also use a boolean flag to do the reflection count and test setup only once at the first call. Hope this helps someone, it took me an afternoon to figure out a better way than enumerating all classes in a suite.
Now all you need to do is extend this class for all your test classes. We already had a base class to provide some common stuff for all our tests so this was the best solution for us.
Inspiration comes from this SO answer https://stackoverflow.com/a/37488620/5930242
If you don't want to extend this class everywhere, this last SO answer might do what you want.

Categories

Resources