How to get rid of TemporaryFolder rule in Junit5 - java

I'm migrating unit tests from Junit4 to Junit5. In the test I'm using TemporaryFolder rule from Junit4 API. To keep the test working I added #EnableRuleMigrationSupport annotation:
#EnableRuleMigrationSupport
public final class SomeTest {
#Rule
public final TemporaryFolder tmp = new TemporaryFolder();
// tests ...
}
As I understand, in Junit5 I need to use extensions instead of rules, but I can't find any replacement for TemporaryFolder in Junit5 extensions. Does it exist? How to correctly replace TemporaryFolder rule with extension?

You can use the #TempDir annotation (JUnit 5.4+), described in ยง2.20.1 of the JUnit 5 User Guide. From the user guide (emphasis mine):
The built-in TempDirectory extension is used to create and clean up a temporary directory for an individual test or all tests in a test class. It is registered by default. To use it, annotate a non-private field of type java.nio.file.Path or java.io.File with #TempDir or add a parameter of type java.nio.file.Path or java.io.File annotated with #TempDir to a lifecycle method or test method.
Note: This extension was added in version 5.4 and is currently (as of 5.8.2) experimental.
Example of using an instance field:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path; // or use java.io.File
class SomeTests {
#TempDir
Path directory; // may be private since 5.8
}
โ€“ Allow #TempDir fields to be private #2687
Example of using a parameter of a test method:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path; // or use java.io.File
class SomeTests {
#Test
void testFoo(#TempDir Path directory) {
// do test...
}
}
Note: Constructor parameters are not supported.
When the directory is created and deleted is described in the Javadoc of #TempDir (documentation quote from JUnit 5.8.2):
Creation
The temporary directory is only created if a field in a test class or a parameter in a lifecycle method or test method is annotated with #TempDir. If the field type or parameter type is neither Path nor File or if the temporary directory cannot be created, an ExtensionConfigurationException or a ParameterResolutionException will be thrown as appropriate. In addition, a ParameterResolutionException will be thrown for a constructor parameter annotated with #TempDir.
Scope
By default, a separate temporary directory is created for every declaration of the #TempDir annotation. If you want to share a temporary directory across all tests in a test class, you should declare the annotation on a static field or on a parameter of a #BeforeAll method.
Old behavior
You can revert to the old behavior of using a single temporary directory by setting the junit.jupiter.tempdir.scope configuration parameter to per_context. In that case, the scope of the temporary directory depends on where the first #TempDir annotation is encountered when executing a test class. The temporary directory will be shared by all tests in a class when the annotation is present on a static field or on a parameter of a #BeforeAll method. Otherwise โ€” for example, when #TempDir is only used on instance fields or on parameters in test, #BeforeEach, or #AfterEach methods โ€” each test will use its own temporary directory.
Deletion
When the end of the scope of a temporary directory is reached, i.e. when the test method or class has finished execution, JUnit will attempt to recursively delete all files and directories in the temporary directory and, finally, the temporary directory itself. In case deletion of a file or directory fails, an IOException will be thrown that will cause the test or test class to fail.

Related

Junit - Testsuite - Classnames from file

I have a junit test suite as below and it works as expected.
#RunWith(Suite.class)
#Suite.SuiteClasses({
abc.class,
xyz.class
})
public class RunSuiteIT {}
Now my requirement is to keep the class names (abc,xyz) in a json file and get it read during run time. Is it possible to do that in Java/JUnit?
Yes, you should be able to do this at runtime, which means you can't use an annotation. Instead you will need a static Suite suite() method that reads the JSON file and uses the reflection API to create the Class instances from the string input.

With JUnit 5, how to share information in `ExtensionContext.Store` between test instances?

I am running into trouble with JUnit 5 (5.0 or 5.1) and custom extension.
We are using service loader to load all implementations which then modify how our extension is bootstrapped. These implementations can be loaded just once, so I was thinking of using ExtensionContext.Store and placing it there. Every subsequent test instance would then just load it from Store instead of via service loader.
Now, I am even aware of the hierarchical context structure and I know that there is some "root" context which you can get through ExtensionContext.getRoot(). But this "root" context (instance of JupiterEngineExtensionContext) isn't really root - there is different one for every test instance.
Say you have FooTest and BarTest, then printing out getRoot() for each of them yields:
org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext#1f9e9475
org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext#6c3708b3
And hence trying to retrieve previously stored information from Store fails.
Is having this limitation intended? It makes the borderline between ClassExtensionContext and JupiterEngineExtensionContext pretty blurred.
Is there another way to globally store some information via extension?
Here is a (very) simplified version of how I tried working with the store (cutting out all other information basically). I also added some System.out.print() calls to underline what I am seeing. Executing this extension on two test classes results in what I described above:
public class MyExtension implements BeforeAllCallback {
#Override
public void beforeAll(ExtensionContext context) throws Exception {
System.out.println(context.getRoot());
if (context.getRoot().getStore(Namespace.create(MyExtension.class)).get("someIdentifier", String.class) == null) {
context.getRoot().getStore(Namespace.create(MyExtension.class)).put("someIdentifier", "SomeFooString");
} else {
// this is never executed
System.out.println("Found it, no need to store anything again!");
}
}
}
EDIT: Here is a minimal project on GH(link), run by mvn clean install, which displays the behaviour I see.
I just copied your MyExtension verbatim (i.e., with zero changes) and ran both FooTest and BarTest.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
#ExtendWith(MyExtension.class)
class FooTest {
#Test
void test() {
}
}
and
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
#ExtendWith(MyExtension.class)
class BarTest {
#Test
void test() {
}
}
And the result is:
org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext#2280cdac
org.junit.jupiter.engine.descriptor.JupiterEngineExtensionContext#2280cdac
Found it, no need to store anything again!
Thus, getRoot() works as documented.
The only explanation for why you see two different roots is that you must be executing the tests in different processes.
Please keep in mind that the root ExtensionContext instance is bound to the current execution of your test suite.
So if you run FooTest and BarTest one after the other in an IDE, that will actually result in two "test suites" with different roots. The same is true if you configure your build tool to fork between test classes.
Whereas, if you execute both test classes together in a single "test suite" (e.g., by telling your IDE to run all tests in the same package or same source tree) you will then see that there is one root like in the output I provided above.
Note, however, that there was an issue with the junit-platform-surefire-provider prior to version 1.0.3, whereby the provider launched the JUnit Platform for each test class. This would give the appearance of forking even though Surefire did not actually start a new JVM process. For details, see https://github.com/junit-team/junit5/pull/1137.

IntelliJ Idea: JUnit Test Class template depending on condition

I often write tests of different types. Depending on test type it might have different setup.
For instance all my service tests have the following annotation under class declaration:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = NONE)
All controller tests have these annotations:
#RunWith(SpringRunner.class)
#WithMockUser
#WebMvcTest(controllers = MyController.class)
Another tests have different setup, etc.
Every time when I create a test I have to copy-paste this part from another test.
I'm looking for a solution that will help me automate this process in IntelliJ Idea.
Q: Is there any way to define JUnit Test Class templates which work differently depending on the type of the test?
Let's say class name ends with word "Service" - its generated test should use one template, if class name ends with "Controller" - its test should use another one, etc.
It is also possible to detect test type by package name or some other conditions like class content.
The JUnit code generation templates can be found under Settings > File And Code templates > Code.
You can't really create separate code templates, but what you could do is add logic to the existing templates. They use Velocity based directives.
So if, for example, we take the existing JUnit 4 template:
import static org.junit.Assert.*;
#parse("File Header.java")
public class ${NAME} {
${BODY}
}
We can modify it to the following:
import static org.junit.Assert.*;
#if($CLASS_NAME.contains("Service"))
//Import whatever you need for services here.
#end
#if($CLASS_NAME.contains("Controller"))
//Import whatever you need for controllers here.
#end
#parse("File Header.java")
#if($CLASS_NAME.contains("Controller"))
#set($CLASS_SUFFIX = ".class" )
#RunWith(SpringRunner.class)
#RunWithMock
#WebMvcTest(controllers = $CLASS_NAME$CLASS_SUFFIX)
#end
#if($CLASS_NAME.contains("Service"))
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = NONE)
#end
public class ${NAME} {
${BODY}
}
This way if you generate a new JUnit 4 test class through the context menu (hit alt-enter on the class name of the class you want to test and generate new test) it will generate different output if the name of the class to test contains 'Controller' or 'Service'. You might want to change that to endswith instead of contains depending on whatever naming conventions you use.
I've left out the actual import statements in both cases, but I'm sure you'll be able to add those.

Java classpath used by Spring

I know a way to print the classpath of a project at runtime like here:
http://www.mkyong.com/java/how-to-print-out-the-current-project-classpath/
But sometimes the main is even too late, for example when using spring.
Is there a way to print something(e.g. classpath) even before Spring starts the injection process?
Providing some context, i am running a unit test in spring as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:/jmsAppContext.xml")
public class TestProjectProvisioningIntegration
{
....
}
It finds correctly the jmsAppContext.xml, but fails to find one of the properties files.
To get the same effect as you have in the link you posted, you could get the classpath in a static initializer (just do the same as the example, only then in a static { ... } block instead of a main method. The JVM will execute the static initializer first, before loading any other classes your class depends on (other than the classes you reference in the static initializer).

Private class unit tests

How to unit test private (means with package visibility) classed in java?
I have a package, only one class is public in this package, other classes are private. How to cover other classed with unit tests? I would like to include unit tests in resting jar.
Create the unit test class in the same package.
For example, If com.example.MyPrivateClass located in src/main/java/com/example/MyPrivateClass.java
Then the test class will be in same package com.example.MyPrivateClassTestCase and will be located in src/test/java/com/example/MyPrivateClassTestCase.java
There are two ways to do this.
The standard way is to define your test class in the same package of the class to be tested. This should be easily done as modern IDE generates test case in the same package of the class being tested by default.
The non-standard but very useful way is to use reflection. This allows you to define private methods as real "private" rather than "package private". For example, if you have class.
class MyClass {
private Boolean methodToBeTested(String argument) {
........
}
}
You can have your test method like this:
class MyTestClass {
#Test
public void testMethod() {
Method method = MyClass.class.getDeclaredMethod("methodToBeTested", String.class);
method.setAccessible(true);
Boolean result = (Boolean)method.invoke(new MyClass(), "test parameter");
Assert.assertTrue(result);
}
}
As indicated in #Kowser's answer, the test can be in the same package.
In Eclipse, and I assume other IDEs, one can have classes in different projects but in the same package. A project can be declared to depend on another project, making the other project's classes available. That permits a separate unit test project that depends on the production project and follows its package structure, but has its own root directory.
That structure keeps the test code cleanly separated from production code.

Categories

Resources