How can you wrap a JUnit 5 Test - junit

In JUnit 4 you could use a Rule to wrap a test so that you could execute code both before and after a test had run. In most cases this could be accomplished with an #Before and #After method or an ExternalResource rule. However some control flow constructs (like try-with-resources) cannot be split into two methods. In most cases, there are alternatives to these constructs which allow you to split them into two methods. For example, with try-with-resources, you can manually acquire and close a resource instead of using a try block.
The specific problem that I have run into is that the database library I use, jOOQ, only has transaction methods that take a callback. (See https://www.jooq.org/doc/latest/manual/sql-execution/transaction-management/) You cannot call something like:
context.startTransaction()
doStuff()
context.commit() // Or rollback()
In JUnit4 this is ok because you can write a rule like so (in Kotlin, but the equivalent works in Java):
class TransactionRule(private val dbSessionManager: DBSessionManager) : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
dbSessionManager.transaction {
base.evaluate()
}
}
}
}
}
Is there anything similar in JUnit 5?

You can write an InvocationInterceptor in place of the JUnit4 rule:
public class TransactionInvocationInterceptor implements InvocationInterceptor {
#Override
public void interceptTestMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
runInTransaction(() -> {
try {
invocation.proceed();
} catch (Throwable t) {
throw new RuntimeException(t);
}
});
}
}
#ExtendWith(TransactionInvocationInterceptor.class)
class InvocationInterceptorTest {
#Test
void test() {
…
}
}
One difference is that interceptTestMethod only wraps the test method, not other lifecycle methods such as beforeEach. It's possible to intercept the other lifecycle methods individually with the other methods in InvocationInterceptor, but not multiple at a time (for example, if you want to call both beforeEach and the test method in one transaction).

From what I understand you can't use the JUnit 5 test lifecycle callbacks as they would require you to follow the doStuff route with context calls Before/After that you indicate won't work.
Would using JUnit 5 Dynamic Tests instead work?
This provides for test factories consisting of collections of dynamic test with a name and an executable (lambda). You could then do something like this:
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
import org.junit.jupiter.api.function.Executable;
#TestFactory
Collection<DynamicTest> transactionTestCollection() {
return Arrays.asList(
dbTest("1st dynamic test", () -> assertTrue(true)),
dbTest("2nd dynamic test", () -> assertEquals(4, 2 * 2))
);
}
private DynamicTest dbTest(String name, Executable tst) {
return dynamicTest(name, () -> dbSessionManager.transaction(tst));
}

Related

How to skip static validation in JUnit test with Mockito

I have a function I need to test:
#Override
public ResponseEntity<BasicResponse> myFunction(HttpServletRequest request, #RequestBody Dto dto, BindingResult result) {
try {
ValidationUtils.invokeValidator(validator, dto, result);
if (result.hasErrors()) {
// do things
} else {
//do things
}
} catch (Exception e) {
// catch
}
//return things;
}
But when I test the function it always go to the catch because of the validator, it says:
"Validator does not support Dto".
I don't care about the validation, I need to test what's inside the try, so I try to use doNothing to skip the validation but I get an error because it's void:
#Test
void TestMyFunction() {
doNothing().when(ValidationUtils.invokeValidator(validator, dto, result));
ResponseEntity<BasicResponse> response = controller.myFunction(request, dto, result);
// assert something
}
Any idea on how to proceed? I can't change the function, I can only write the test and I'm new at this. Thank you.
If ValidationUtils was an object instance that is a Mockito spy or mock, you would be able to mock it like so:
doNothing()
.when(validationUtils)
.invokeValidator(validator, dto, result);
This, unfortunately, does not seem to be the case in your test - ValidationUtils seems to be a class on which a static method is called, which Mockito can handle since version 3.4.0 (before that it was usually handled by PowerMock). thenAnswer should be used with the mockedStatic object instance, similarly to this answer, since the method's return type is void.
An important additional (but mandatory) note: mocking static methods is discouraged and should be avoided whenever possible. It slows down the tests and is a sign of bad design of the classes. The validator should be injected into the class or created using a factory, thanks to that a mocked object instance could be easily used.
I've checked that and in fact the thenAnswer part is not required to do nothing after using mockStatic on a class.
try (var mockedStatic = Mockito.mockStatic(ValidationUtils.class)) {
// test code
}
The code above makes ValidationUtils do nothing and return default values (0, false, null).
See this example (all tests pass).

JUnit 5 -- how to make test execution dependent on another test passing?

Student here. In JUnit 5, what is the best way to implement conditional test execution based on whether another test succeeded or failed? I presume it would involve ExecutionCondition, but I am unsure how to proceed. Is there a way to do this without having to add my own state to the test class?
To note, I am aware of dependent assertions, but I have multiple nested tests that represent distinct substates, and so I would like a way to do this at the test level itself.
Example:
#Test
void testFooBarPrecondition() { ... }
// only execute if testFooBarPrecondition succeeds
#Nested
class FooCase { ... }
// only execute if testFooBarPrecondition succeeds
#Nested
class BarCase { ... }
#Nested tests give the test writer more capabilities to express the
relationship among several groups of tests. Such nested tests make use
of Java’s nested classes and facilitate hierarchical thinking about
the test structure. Here’s an elaborate example, both as source code
and as a screenshot of the execution within an IDE.
As stated from JUnit 5 documentation, #Nested is use for convenient display in your IDE. I would rather use Assumptions for your preconditions inside a Depentent Assertions.
assertAll(
() -> assertAll(
() -> assumeTrue(Boolean.FALSE)
),
() -> assertAll(
() -> assertEquals(10, 4 + 6)
)
);
You can fix the problem by extracting common precondition logic in a #BeforeEach/#BeforeAll setup method, then use assumptions, which was developed exactly for the purpose of condition test execution. Some sample code:
class SomeTest {
#Nested
class NestedOne {
#BeforeEach
void setUp() {
boolean preconditionsMet = false;
//precondition code goes here
assumeTrue(preconditionsMet);
}
#Test // not executed when precodition is not met
void aTestMethod() {}
}
#Nested
class NestedTwo {
#Test // executed
void anotherTestMethod() { }
}
}

Ignoring invoking internal static call

public static ResponseBean call(Bean bean) throws Exception {
// statements...
IgnoreCall.ignoreMethodCall(bean);
// statements...
// return
}
With the code snippet above, is it possible to test the method ignoring invocation of IgnoreCall.ignoreMethod(Bean) without needing to place the entire statement under a boolean condition?
Here's the unit test code snippet:
#RunWith(PowerMockRunner.class)
#PrepareTest
public ClassHelperTest {
#Test
public void testCall() throws Excpetion {
// stubbing...
ResponseBean responseBean = ClassHelper.call(bean);
// verify/ies
// assert/s
}
}
Notes:
Refactoring ClassHelper.call(Bean) should be avoided. Even with a bad OO design, refactoring is costly.
Method signature is locked unless another pattern is applicable for replacement.
Tried using Mockito.when and PowerMockito.when on the target static method, stubbing didn't work on run-time debug.
As your comments indicate that changing your production code is not possible, you "simply" have to dive into the static-mocking aspects of PowerMock; as outlined here for example.
Basically you need to enable IgnoreCall for static mocking; and then you make calls to ignoreMethodCall() a no-op.
But as you keep asking: the core problem with your question is the fact that you want to mock out a static method that is void. I have a complete example below, but before that some explanations.
The point is: you call a method for two reasons:
It has a side effect
It returns a value, and maybe, causes a side effect, too
A void method can only be called for side effects. And the thing is: when you do static mocking, then that works on class level.
Meaning: you instruct PowerMock to "prevent" any of the static methods of some class from execution; you simply "erase" the side effects of all those static methods! So, by telling PowerMock to do those static mocks, all void methods are already "gone".
But as said, you might also call methods for their return value. And then is when the when() method of Mockito kicks in. You use that method to say: when that value-returning method is invoked, then do this or that.
Long story short; here is a [mcve] using the elements you asked for:
package ghostcat.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
class IgnoreCall {
public static void ignoreMethodCall(Object o) {
System.out.println("SHOULD NOT SHOW UP: " + o);
}
}
class CuT {
public static Object call(Object bean) {
System.out.println("statement1");
IgnoreCall.ignoreMethodCall(bean);
System.out.println("statement2");
return "whatever";
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(IgnoreCall.class)
public class PMTest {
#Test
public void test() {
PowerMockito.mockStatic(IgnoreCall.class);
CuT.call("yeha");
}
}
As in your example ... there is IgnoreCall; used within that a static method that I just called "call".
This prints:
statement1
statement2
When I go in and comment out
// PowerMockito.mockStatic(IgnoreCall.class);
It prints:
statement1
SHOULD NOT SHOW UP: yeha
statement2
So, a simple example that should tell you exactly what you need to do.
I worked with eclipse neon, IBM java8 JDK, and simply imported all the JARs from powermock-mockito-junit-1.6.6.zip into my test project.

Multiple instance of DynamoDbLocal for each unit test class or singleton instantiation

I am trying to use DynamoDbLocal server for unit test cases.
And came up with two options,
Either define a junit class rule which starts local server before class and stops it after class. So essentially it will start and stop server for each unit test class.
public class MyDynamoDbLocalServerRule extends ExternalResource {
#Override
protected void before() throws Throwable {
myInMemoryDynamoDbServer.start();
}
#Override
protected void after() throws Throwable{
inMemoryDynamoDbServer.stop();
}
OR
Singleton instance :
public static DynamoDBProxyServerContainer createInstance(final int portToListenIn) {
if (dynamoLocal == null) {
synchronized (DynamoDBProxyServerContainer.class) {
if (dynamoLocal == null) {
dynamoLocal = new DynamoDBProxyServerContainer(portToListenIn);
}
}
}
return dynamoLocal;
}
private DynamoDBProxyServerContainer(final int portToListenIn) {
this.startServer(portToListenIn);
getRuntime().addShutdownHook(new Thread(() -> stopServer()));
}
Which one would you recommend and do you have better of doing this ? Please note i should be able to use it with Guice dependency injection framework.
I would recommend Singleton approach as creating the database instance for each test case will be a time consuming option. Also, if you have many test cases, the unit testing is likely to take more time to complete. If you have continuous integration, the build and unit test would take more time.
As the unit tests run in sequential manner, you don't need separate instance for each test case.

Passing command line arguments to JUnit in Eclipse

All,
I am currently using JUnit 4 for writing test cases. I am fairly new to JUnit and finding it difficult to test my main class which takes arguments. I have specified the arguments to my JUnit test class by:
1 > Right click JUnit test class
2 > Goto Run As -> Run Configurations
3 > Select the Arguments tab and specify a value (I have entered an invalid argument i.e. the main class expects the command line argument to be converted to an int and I am passing a String value that cannot be converted to int)
However, the main class that I am testing, if the command line argument cannot be converted to a int, than I throw IllegalArgumentException. However, the JUnit does not show the testMain() method as Error or Failure. I don't think my setup is right for the JUnit class. Can anyone please guide me where I am going wrong
To test your class main method simply write something like:
#Test(expected = IllegalArgumentException.class)
public void testMainWithBadCommandLine()
{
YourClass.main(new String[] { "NaN" });
}
Change the main() method to something like this:
public static void main(String[] args)
{
MyClass myclass = new MyClass(args);
myclass.go();
}
Move the code that was in main() to the new method go(). Now, your test method can do this:
public void myClassTest()
{
String[] args = new String[]{"one", "two"}; //for example
MyClass classUnderTest = new MyClass(testArgs);
classUnderTest.go();
}
Firstly the arguments should be in the program arguments section. Normally the launching point of the application that's the main method doesn't need to be tested if you design the app to be testable.
Refactor the class
public static class ArgumentValidator
{
public static boolean nullOrEmpty(String [] args)
{
if(args == null || args.length == 0)
{
throw new IllegalArgumentException(msg);
}
//other methods like numeric validations
}
}
You can now easily test the nullOrEmpty method using junit like
#Test(expected = IllegalArgumentException.class)
public void testBadArgs()
{
ArgumentValidator.nullOrEmpty(null);
}
I think this is a better approach