I need to create a version 2 of an existing service endpoint. In creating the unit tests, I realized the most efficient way to test the new code was to extend from the existing unit test class.
The setup, teardown, and stubbing methods were already completed and are pretty complex. I don't want to copy them into the new unit tests for redundancy reasons and I cannot move them into a utilities class since there's some tightly coupled logic in some of the stubbing classes.
When I run my new unit tests I am seeing the inherited unit tests run as well, which is not what I wanted. Has anyone been successful in inheriting from a base unit test without running any of its #Test classes?
Here's an example of my base class :
#SpringBootTest
#ContextConfiguration
#ExtendWith(MockitoExtension.class)
#AutoConfigureMockMvc
public class MyBaseClass extends TestBase {
#Value("${test.foo}")
private String foo
// setup, teardown, stubbing to pull in data to run the tests AND then the tests themselves
}
#SpringBootTest
#ContextConfiguration
#ExtendWith(MockitoExtension.class)
#AutoConfigureMockMvc
public class MyVersion2BaseClass extends MyBaseClass {
/* JUST THE TESTS, NO SETUP/TEAR DOWN/STUBBING NEEDED
AS THIS IS CONTAINED IN THE PARENT CLASS
*/
}
I ended up removing all the setup/tear down/stubbing code into an abstract class.
Now my two versions of test just extend from the abstraction and all the administrative code is available to them.
Related
A set of tests should be run on every microservice. Current solution is to have an abstract class and extend in every service, providing the necessary properties in abstract getters.
public abstract class AbstractTest {
#LocalServerPort
protected int serverPort;
protected abstract String getPath();
#Test
void someTest() {}
#Test
void conditionalTest() {}
}
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
classes = {...})
#ActiveProfiles(...) // etc
public class MyTest extends AbstractTest {
// ... implement getPath()
// tests from parent will be executed
}
The goal:
Ditch inheritance and have the AbstractTest's logic get executed automatically with conditional #Test execution based on beans/properties etc.
The possible solution:
A concrete class with all the tests or some sort of Configuration/TestFactory to create the necessary tests. It should take into account available properties and beans to determine which tests to run.
The problem:
How can those tests (created in runtime) be discovered and registered for execution?
How to inject all the properties that are part of the current context of the #SpringBootTest?
Failed attempts:
TestInstanceFactory extension doesn't seem to be the solution as it requires an instance of the class which it annotates.
Using the Launcher API seems overkill, and also doesn't seem to work, since the library class won't be created with the Spring context configs.
using cglib and a base class Spring Contract-style is not a desirable solution
Ideally I don't want the client of this lib to implement/create anything, so abstract String getPath(); would be a test.lib.path property, and if it's present, a test from the library which uses it will run.
Any thoughts on this would be great, because right now this just seems impossible to me.
What is the reason to have the inheritance for tests?
In case you need to share some common logic within the tests you may try JUnit features (custom rules/extensions), for example
For junit < 5.x.x #Rule functionality https://junit.org/junit4/javadoc/4.12/org/junit/rules/TemporaryFolder.html https://stackoverflow.com/a/34608174/6916890
For junit >= 5.x.x (jupiter) there is an extension API
https://junit.org/junit5/docs/current/user-guide/#writing-tests-built-in-extensions-TempDirectory
I have a legacy test class using old Junit 3 way:
public class MyTestUtil extends TestCase {
//class has helper methods but no method starting with "test"
}
I have other test classes which extends this class:
public class MyTests extends MyTestUtil {
public void testSomething() {
}
}
I am trying to run this using gradle build file. And the build fails complaining with a warning:
junit.framework.AssertionFailedError: No tests found in myPackage.MyTestUtil
The build obviously runs fine when I exclude this class from test task:
test {
exclude '**/MyTestUtil.class'
}
But I don't know if excluding like this is the only solution.
Is there a way to do away with this warning?
From the JUnit FAQ:
Why do I get the warning "AssertionFailedError: No tests found in XXX" when I run my test?
Make sure you have more or more method annotated with #Test.
Either add at least one test or exclude the class like you described.
I have the following test class:
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HierarchicalTest {
#Test
void checkOuter() {
assertTrue(false);
}
#Nested
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class PrepBTest {
#Test
void checkInnerA() {}
#Test
void checkInnerB() {}
}
}
I want to have the behavior that checkInnerA() and checkInnerB() won't be executed when checkOuter() fails.
On the other side checkInnerB() should be executed when checkInnerA() fails because it is on the same level.
Is there a simple soulution (e.g. with JUnit 5 extension) to achieve this behavior?
In my opinion that's often the behavior which is wished.
UPDATE:
As of JUnit Jupiter 5.4, you can develop an extension that implements the TestWatcher and ExecutionCondition APIs to achieve this behavior.
In the testFailed() method from the TestWatcher API you need to track test classes that have failures, and you need to store this information in the root ExtensionContext.Store.
In the evaluateExecutionCondition() method from the ExecutionCondition API you need to determine if the current class is a #Nested test class (i.e., an inner class) and check if the enclosing test class had failures. If that holds true, you need to disable the current #Nested test class and otherwise enable it.
Those are the general guidelines. For a working example, please see the SkipOnFailuresInEnclosingClassExtension I just posted to my junit5-demo repository on GitHub. That example goes one step further by only skipping #Nested test classes if they are also annotated with #SkipOnFailuresInEnclosingClass. The OuterTests class shows the annotation and extension in action.
No, as of JUnit Jupiter 5.3, there is currently no way to achieve that with out-of-the-box solutions.
You could potentially write a custom extension that tracks the success of tests in an enclosing test class -- for example, by implementing TestExecutionExceptionHandler. That would need to be stored in the ExtensionContext.Store. The extension would then need to implement ExecutionCondition to programmatically disable nested test classes.
It's unfortunately not very straightforward to track the "success" of previously executed tests currently, but that should improve with the introduction of the new TestWatcher extension API that is currently slated for inclusion in the upcoming JUnit Jupiter 5.4: https://github.com/junit-team/junit5/issues/542
I found a strange thing and I'm interested to know why it happens. I'm using maven surefire plugin ( 2.12.4 ) with Junit 4.11. When I wanted to use #Category annotation in order to disable some tests. the strange thing that it works correctly only with tests that don't extend TestCase. For test that don't extend TestCase I was able to put the annotation only on the test method to run/disable, but with others it disables all tests in the class.
Example:
Command Line:
mvn test -Dgroups=!testgroups.DisabledTests run only test B for the first snippet:
import static org.junit.Assert.*;
public class DataTest {
#Test
public void testA(){...}
#Test #Category(testgroups.DisabledTests.class)
public void testB(){...}
}
for the second case with class extending TestCase, it will run no tests.
public class DataTest extends TestCase {
#Test
public void testA(){...}
#Test #Category(testgroups.DisabledTests.class)
public void testB(){...}
}
Why it happens?
The solution was given in a comment by deborah-digges:
The problem is that the second class is an extension of TestCase. Since this is JUnit 3 style, the annotation #Category didn't work. Annotating the class with #RunWith(JUnit4.class) should give you the same result in both cases
and another by Stefan Birkner:
JUnit 4 finds test by looking for the #Test annotation. You can remove the extends TestCase from your test class. Furthermore the name of the test method does no longer have to start with test. You're free to choose any method name you want.
I am seeking for a way to create and let run a JUnit TestSuite in a non-static fashion.
Currently I am doing something like this:
public class MyTestSuite {
public static TestSuite suite() {
TestSuite suite = new TestSuite();
suite.addTest(...);
suite.addTest(...);
// ....
return suite;
}
}
I am doing this because I am creating the TestCases I am adding to the suite programmatically.
With this solution I am facing the problem that my class MyTestSuite is never instantiated. I would like to wire it with a spring container, e.g. using
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={...})
#Transactional
but I see no way of telling the SpringJUnit4ClassRunner that it should also execute my programmatic tests.
Thanks for your help!
Erik
Why use a suite at all? Seems simpler to put your tests in their own subdirectory and have an ant (or whatever build tool you're using) target that runs just the tests found there.
You could try and have MyTestSuite as part of your spring context (the test context) and fire an init method on it which would add your programmatic tests. That would allow you to inject MyTestSuite which has this programmtic tests added when it is instantiated by spring.
Hope that helps.
For JUnit3-style suite methods, JUnit does not create an instance of the class; it calls the method and calls run(TestResult) on the returned object.
SpringJUnit4ClassRunner is a JUnit4 Runner class, so it cannot be used to affect the behavior of JUnit3-style test suites. Spring does not provide a JUnit4-style suite implementation. If you want each of the test cases to use SpringJUnit4ClassRunner, your best option is to upgrade them to JUnit4.
If you are asking how you add your Spring tests to MyTestSuite:
public class MyTestSuite {
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(...);
suite.addTest(...);
suite.addTest(new JUnit4TestAdapter(ExampleSpringTest.class));
// ....
return suite;
}
}