How to close resources shared between junits? - java

I have many junit 4 test classes with many tests.
Many of these tests need a DataSource to access a database.
To speed up tests, I would like to share a single DataSource for all test classes.
That would avoid connection cost.
So I have put the DataSource in a static variable.
But I have not found a way to close it!
With #Rule + TestWatcher, we can execute code before and after a single test...
With #ClassRule + TestWatcher, we can execute code before and after a single test class...
But how to execute code after the execution of all executed test classes ?
The test suite doesn't seems appropriate, because I am executing either all, one, or any subset of the junits tests.
Any idea?

You can use ClassRule
public static class GlobalDataSource extends ExternalResource
private int count = 0;
private DataSource dataSource;
private GlobalDataSource() {};
public static final GlobalDataSource INSTANCE = new GlobalDataSource();
public DataSource get() {
if (dataSource == null) {
throw new IllegalStateException();
}
return dataSource;
}
#Override
protected void before() throws Throwable {
if (count++ == 0) {
dataSource = createDataSource();
}
}
#Override
protected void after() {
if (--count == 0) {
try {
destroyDataSource(dataSource);
} finally {
dataSource = null;
}
}
};
};
In your tests:
#RunWith(JUnit4.class)
public class FooTest {
#ClassRule public static GlobalDataSource source = GlobalDataSource.INSTANCE;
#Test
public void readDataSource() {
...
}
}
Then create a suite:
#RunWith(Suite.class)
#SuiteClasses(FooTest.class, BarTest.class ...)
public class AllTests {
#ClassRule public static GlobalDataSource source = GlobalDataSource.INSTANCE;
}
Be aware that global state in tests can be just as problematic as global state in code. A failure in one test could lead other tests to fail.

Installing JVM shutdown hook does not appear too ugly in that case.
Far more reliable than a finalize() method called only if the object is garbage collected.
Not called if process interrupted, but in that case there nothing can be done anyway. So I did not find major drawbacks.
Thanks.

Related

Mocked method is used even after teardown on other tests when using MockUp from jmockit

I am using jmockit version 1.24 with junit5 where I am mocking a public method of a singleton class as shown below.
Here's my Test.java:
#Test
void myTest() {
MockUp mySingletonMock = new MockUp<MySingleton>() {
#Mock
public FileHeaders getHeader(String localFilePath) {
return new FileHeaders(checksum, "", "", new Date());
}
};
// Some assert statements
mySingletonMock.tearDown();
}
And this is Singleton.java:
public class MySingleton {
private static MySingleton instance = new MySingleton();
private MySingleton(){
// Some initialization
}
public static MySingleton getInstance(){
return instance;
}
public FileHeaders getHeader(String localFilePath) {
...
}
}
I am facing a problem with the above approach where all tests that execute after myTest completes execution fail as they still see the mocked getHeader method instead of the original one in the MySingleton class (I have verified that this is indeed the case using debug statements).
How to prevent this mocked version of getHeader method being seen in other tests? (preferably without changing the version of jmockit).
The weird part of all this is that the tests run without any issue locally on my system using maven. But fail when run on teamcity.
Any help is appreciated. Thank you.
Edit:
Things I have tried:
I have tried adding the $clinit() method to the MockUp. But no luck.
I have reset the singleton instance to a new instance through reflection at the end of my test as shown below. This did not solve the problem either.
void resetMySingletonInstance() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Constructor<?>[] constructors = MySingleton.class.getDeclaredConstructors();
Constructor theConstructor = constructors[0];
theConstructor.setAccessible(true);
// Verified that this gives a new instance
MySingleton instanceMySingleton = (MySingleton) theConstructor.newInstance();
Field ourInstanceField = MySingleton.class.getDeclaredField("ourInstance");
ourInstanceField.setAccessible(true);
ourInstanceField.set(null, instanceMySingleton);
}
First I would not initialize and tear down the mocks in the test methods directly, but instead use setup() and tearDown() methods (#Before and #After annotations).
private List<MockUp<?>> mockUps = new ArrayList<>();
public void addMockUp(MockUp<?> mockUp) {
mockUps.add(mockUp);
}
#Before
public void setup() throws Exception {
addMockUp(new MockUp<MySingleton>() {
#Mock
public FileHeaders getHeader(String localFilePath) {
return new FileHeaders(checksum, "", "", new Date());
}
});
// other mocks ?
}
#Test
void myTest() {
// Some assert statements on MySingleton.getInstance()
}
#After
public void tearDown() {
mockUps.stream().forEach(mock->mock.tearDown());
}
But the issue in your test is that MySingleton is initialized with a mock object. If all your tests are executed in the same VM, this mock object will be reused for subsequent tests.
I'm not sure why you don't have this issue locally but only on your build server, maybe configuration of VM forking : https://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html
To fix this, you need to find a way to reinitialise the singleton after each test.
For example, one solution would be to change MySingleton to be able to reset the instance after the test. Provided MySingleton is not accessed concurrently, you could use lazy loading for example and reset it in the tearDown method.
public class MySingleton {
private static MySingleton instance;
private MySingleton(){
// Some initialization
}
public static MySingleton getInstance(){
if(instance == null) {
instance = new MySingleton();
}
return instance;
}
public static void reset() {
instance = null;
}
public FileHeaders getHeader(String localFilePath) {
...
}
}
You have also another option if you can't change production code to reset the field by reflection:
Set Value of Private Static Field

How to use #BeforeClass and #AfterClass within a JerseyTest suite

It turns out that JUnit wants #BeforeClass and #AfterClass to be static and this doesn't get along well with JerseyTest's configure method override. Is there a known way to configure the Jersey application while still being able to access JUnit's utility methods?
public class MyControllerTest extends JerseyTest {
#BeforeClass
public static void setup() throws Exception {
target("myRoute").request().post(Entity.json("{}"));
}
#Override
protected Application configure() {
return new AppConfiguration();
}
}
Hence beforeClass needs to be static, target cannot be called because of its instance-method nature. While trying to use the constructor instead, it turns out that configure is run after the constructor and this prevents the setup-request to be executed and will therefor fail naturally.
Any advice is more than appreciated, thanks!
What we did in several cases to avoid heavy setups in such situations is to use a boolean flag to run that setup conditionally.
public class MyControllerTest extends JerseyTest {
private static myRouteSetupDone = false;
#Before
public void setup() throws Exception {
if (!myRouteSetupDone) {
target("myRoute").request().post(Entity.json("{}"));
myRouteSetupDone = true;
}
}
#Override
protected Application configure() {
return new AppConfiguration();
}
}
#Before does not require the static modifier and will be executed before every test-method.

JUnit: #Rule's overridden apply method not executed under test

I created a JUnit rule to rollback a javax.persistence transaction when any exception occurs (otherwise all further tests will fail since the transaction is in an inconsistent condition). The problem is that my rule will never be executed when the test starts, strictly speaking: the apply method is never executed. it does not even work when I put the #Rule declaration into the concrete class and initialize the transactionRule within the test. Here is how it looks like:
THE RULE
public class TransactionRule implements TestRule
{
private EntityManager entityManager;
public TransactionRule(EntityManager entityManager)
{
this.entityManager = entityManager;
}
#Override
public Statement apply(Statement base, Description description)
{
return new TransactionStatement(base);
}
public class TransactionStatement extends Statement
{
private final Statement runningTest;
public TransactionStatement(Statement runningTest)
{
this.runningTest = runningTest;
}
#Override
public void evaluate() throws Throwable
{
try
{
runningTest.evaluate();
}
catch (Exception e)
{
if (entityManager.getTransaction().isActive())
{
entityManager.getTransaction().rollback();
}
}
}
}
}
THE ABSTRACT CLASS THAT USES THE RULE
public abstract class AbstractIntegrationTest
{
//more members vars
#Rule
public TransactionRule transactionRule;
#BeforeClass
public static void setUpBeforeClass()
{
loadProperties();
entityManagerFactory = Persistence.createEntityManagerFactory("MyCuisinePersistenceTestUnit", connectionProperties);
entityManager = entityManagerFactory.createEntityManager();
}
#Before
public void setUp()
{
transactionRule = new TransactionRule(entityManager);
entityManager.clear();
}
//more code
}
THE TEST CLASS WITH THE DEFECTIVE TEST
public class RecipePersistenceITest extends AbstractIntegrationTest
{
//more tests
#Test
public void persistenceOfRecipeWithUserCategorySuccessful()
{
//Test that fails
}
}
Any ideas?
Test rules are invoked before methods annotated with #Before, so trying to assign a rule in #Before will have no effect (although I would have expected it to throw an exception instead).
Instead assign the rule on definition (and make the field final), and do any additional config (if necessary) in your #Before.
Note that each test method is executed in a new instance of the test class, so defining a rule as a final field is no problem.

How should a custom Guice scope be integrated with TestNG?

We use a custom Guice scope, #TestScoped, for some of our JUnit tests that lasts for a single test method, and a JUnit #Rule to enter and exit the scope appropriately. It looks like this:
public class MyJUnitTest {
#Rule public CustomRule customRule = new CustomRule(MyModule.class);
#Inject private Thing thing;
#Test
public void test1() {
// Use "thing"
}
#Test
public void test2() {
// Assuming "Thing" is #TestScoped, we'll have a new instance
}
}
We're starting to use TestNG for some of our tests in other projects, and we'd like to have a similar pattern. So far we've come up with this:
#Listeners(CustomTestNGListener.class)
#Guice(modules = MyModule.class)
public class MyTestNGTest {
#Inject private Provider<Thing> thingProvider;
#Test
public void test1() {
Thing thing = thingProvider.get();
// Use "thing"
}
#Test
public void test2() {
Thing thing = thingProvider.get();
// Assuming "Thing" is #TestScoped, we'll have a new instance
}
}
public class CustomTestNGListener implements IHookable {
#Override
public void run(IHookCallBack callBack, ITestResult testResult) {
TestScope.INSTANCE.enter();
try {
callBack.runTestMethod(testResult);
} finally {
TestScope.INSTANCE.exit();
}
}
}
There are a couple issues with this design:
Unlike JUnit, TestNG uses the same instance of the test class for each method. That means we have to inject Provider<Thing> instead of just Thing, which is awkward.
For some reason, CustomTestNGListener is running on all of our tests, even ones that don't have that #Listeners(CustomTestNGListener.class) annotation. I've worked around this by explicitly checking for that annotation in the listener itself, but it feels like a hack (though I do see that MockitoTestNGListener does the same thing).
Does someone with more familiarity with TestNG have any suggestions for dealing with these issues?
Instead of
public class MyTestNGTest {
#Inject private Provider<Thing> thingProvider;
#Test
public void test1() {
Thing thing = thingProvider.get();
In TestNG you can used
public class MyTestNGTest {
#Inject
private Thing thingInjected;
private Thing thing;
#BeforeTest
public void doBeforeTest() {
thing = thingInjected.clone();
}
Or just call thingProvider.get() in doBeforeTest(), it's better in you have a lot of # Test
public class MyTestNGTest {
#Inject private Provider<Thing> thingProvider;
private Thing thing;
#BeforeTest
public void doBeforeTest() {
thing = thingProvider.get();
}

Stopping JUnit suite if particular test fails

I have a JUnit test suite in the form:
#RunWith(Suite.class)
#Suite.SuiteClasses( { xx.class, yy.cass })
public class AllTests {
public static Test suite() {
TestSuite suite = new TestSuite(AllTests.class.getName());
//$JUnit-BEGIN$
//$JUnit-END$
return suite;
}
}
This then calls vanilla tests like this:
public class xxx {
#Test
public void test () throws {
...
I have a situation where I'd like to stop the rest of the test suite running if there's an error or fail in the first test. But errors / fails in the others are ok and the suite should complete as many other tests as it can. Basically the first test failing would indicate it isn't safe to run the rest.
Is this possible?
First you need junit RunListener:
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
public class FailureListener extends RunListener {
private RunNotifier runNotifier;
public FailureListener(RunNotifier runNotifier) {
super();
this.runNotifier=runNotifier;
}
#Override
public void testFailure(Failure failure) throws Exception {
super.testFailure(failure);
this.runNotifier.pleaseStop();
}
}
Then prepare a suite:
public class StopOnFailureSuite extends Suite {
public StopOnFailureSuite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
super(klass, suiteClasses);
}
public StopOnFailureSuite(Class<?> klass) throws InitializationError {
super(klass, klass.getAnnotation(SuiteClasses.class).value());
}
#Override
public void run(RunNotifier runNotifier) {
runNotifier.addListener(new FailureListener(runNotifier));
super.run(runNotifier);
}
}
And run your suite:
#RunWith(StopOnFailureSuite.class)
#Suite.SuiteClasses({
FirstTestClass.class,
SecondTestClass.class,
...
})
What's wrong with calling System.exit()?
If it's first test then consider moving its validation to #BeforeClass and throw exception if it fails. Then only #AfterClass method would run in case of this exception.
Of course, that way you lack all the fixture artifacts created in test setup method(s).
Like your answer but using #Before in an integration test, I did something like this:
public class FooTest {
private static boolean bar;
#BeforeClass
public static void setUpBeforeClass() throws Exception {
bar = false;
}
#Before
public void setUp() throws Exception {
assertTrue(bar);
}
#Test
public void test() {
System.out.println("something");
assertTrue(true);
}
#Test
public void test1() {
System.out.println("Something2");
assertTrue(true);
}
}
Regards!
Based on the answer from Hiro2k (thanks!) I've used the following solution. It's a bit of a hack but it works.
The test which can prevent other tests running goes at the top of the #Suite.SuiteClasses list. That test then has the following:
private static boolean shouldStopRestOfSuite = false;
#Test
public void test () throws Throwable {
try {
... run some test code...
}
catch (Throwable e) {
shouldStopRestOfSuite = true;
throw e;
}
}
Note the above does need to catch Throwable (not exception) so it catches assertion errors. It also re-throws the error so it's logged by JUnit for analysis.
Then there's another test method:
#Test
public void testKillIfNeeded () throws Exception {
if (!shouldStopRestOfSuite) {
return;
}
System.out.println ("Test suite killed due to dangerous error / failure");
System.exit(1);
}
The above is run second and will kill the JUnit process.
Using this method the JUnit test won't end on fail / error if there's an issue but the fail / error is logged for analysis by JUnit and no further tests will run.
Not too pretty but it does the job :)
Firstly you should catch an error and check the same before you run the 2nd test.
#Rule
public ErrorCollector collector = new ErrorCollector();
1. Add Error.
collector.addError(new Throwable("first thing went wrong"));
2. Check before the dependent run.
collector.checkThat(getResult(), not(containsString("ERROR!")));
Reference - ErrorCollector
Are you running tests using ant?
You could write a custom test listener. You can set this in ant http://ant.apache.org/manual/Tasks/junit.html ( enableTestListenerEvents).
I find it troubling that this functionality is so tedious to implement in such a mature library. If you're using JUnit 5 / Jupiter you can use an extension called JUnit Pioneer (https://junit-pioneer.org).
With JUnit Pioneer you can simply add a #DisableIfTestFails annotation to your test class to make all tests stop when one fails.

Categories

Resources