Repository: iluwatar/java-design-patterns
Issue: Use the same JUnit version everywhere
Labels: info: help wanted
epic: build issue
epic: dependencies
type: refactoring

At the moment there are some modules that are still using JUnit 4. Most of the codebase uses JUnit 5, but even with that there are different versions. For example `unit-of-work` and `partial-response` declare their own versions.

In this ticket let's try to apply the same version of JUnit 5 everywhere upgrading from JUnit 4 where needed.


## This issue has been partially addressed using #1542 and #1668

Tests from two modules [**serverless**](https://github.com/iluwatar/java-design-patterns/tree/master/serverless) and [**naked-objects**](https://github.com/iluwatar/java-design-patterns/tree/master/naked-objects) are not being executed.

Respective issues have been opened for resolving the tests in favour of these two modules.
- [x] #1667 against [**serverless**](https://github.com/iluwatar/java-design-patterns/tree/master/serverless)
- [ ] #1669 against [**naked-objects**](https://github.com/iluwatar/java-design-patterns/tree/master/naked-objects)

Resolving these two issues should close this [current issue](https://github.com/iluwatar/java-design-patterns/issues/1500)

Issue comments:
I'd like to take this issue. Thank you in advance.
Yes @ToxicDreamz, go ahead ☺️ 
Half way through upgrading, one test case seems to be causing an issue, within the **Serverless** module, in specific the **`SavePersonApiHandlerTest#handleRequestSavePersonException()`** method. I do not have any knowledge of the AWS library, which is why I chose not to mess around with it. 

Initially the DynamoDBMapper was being Mocked with Mockito, and was causing issues once the tests were upgraded to JUnit 5 stating that the class could not be mocked, but as stated in the official Mockito guidelines, a type **should not be mocked if it doesn't belong to you**. See attached link below for details.

https://github.com/mockito/mockito/wiki/How-to-write-good-tests#dont-mock-a-type-you-dont-own

### Suggestion.
A new issue should be opened to address that certain test case, as of now the test case is commented out and marked with a **_TODO_**


### Displayed Error

```
> 
> Caused by: com.amazonaws.SdkClientException: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.
> 	at com.amazonaws.client.builder.AwsClientBuilder.setRegion(AwsClientBuilder.java:371)
> 	at com.amazonaws.client.builder.AwsClientBuilder.configureMutableProperties(AwsClientBuilder.java:337)
> 	at com.amazonaws.client.builder.AwsSyncClientBuilder.build(AwsSyncClientBuilder.java:46)
> 	at com.iluwatar.serverless.baas.api.SavePersonApiHandlerTest.<clinit>(SavePersonApiHandlerTest.java:58)
> 	... 53 more
```

Error after supplying region`

```
> com.amazonaws.SdkClientException: Unable to load AWS credentials from any provider in the chain
> 
> 	at com.amazonaws.auth.AWSCredentialsProviderChain.getCredentials(AWSCredentialsProviderChain.java:131)
> 	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.getCredentialsFromContext(AmazonHttpClient.java:1164)
> 	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.runBeforeRequestHandlers(AmazonHttpClient.java:762)
> 	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:724)
> 	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:717)
> 	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:699)
> 	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:667)
> 	at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:649)
> 	at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:513)
> 	at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.doInvoke(AmazonDynamoDBClient.java:3082)
> 	at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.invoke(AmazonDynamoDBClient.java:3058)
> 	at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.executePutItem(AmazonDynamoDBClient.java:2119)
> 	at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.putItem(AmazonDynamoDBClient.java:2094)
> 	at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper$SaveObjectHandler.doPutItem(DynamoDBMapper.java:883)
> 	at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper$1.executeLowLevelRequest(DynamoDBMapper.java:536)
> 	at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper$SaveObjectHandler.execute(DynamoDBMapper.java:733)
> 	at com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.save(DynamoDBMapper.java:623)
> 	at com.amazonaws.services.dynamodbv2.datamodeling.AbstractDynamoDBMapper.save(AbstractDynamoDBMapper.java:123)
> 	at com.iluwatar.serverless.baas.api.SavePersonApiHandler.handleRequest(SavePersonApiHandler.java:50)
> 	at com.iluwatar.serverless.baas.api.SavePersonApiHandlerTest.handleRequestSavePersonSuccessful(SavePersonApiHandlerTest.java:76)
> 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> 	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> 	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
> 	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
> 	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
> 	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
> 	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
> 	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
> 	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
> 	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
> 	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
> 	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
> 	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
> 	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
> 	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
> 	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
> 	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
> 	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
> 	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
> 	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
> 	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
> 	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
> 	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
> 	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
> 	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
> 	at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
> 	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
> 	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
> 	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
> 	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
> 	at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
> 	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
> 	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
> 	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
> 	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
> 	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
> 	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
> 	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
> 	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
> 	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
> 	at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
> 	at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
> 	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
> 	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
> 	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
> 	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
> 	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
> 	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

```


Overall the serverless module needs to be looked at, more tests are causing the same issues due to them not being able to be mocked.
@ToxicDreamz FYI: I created #1505 which removes the dependency of `subclass-sandbox` to Rule, which is a JUnit 4 only feature.
@stefanbirkner Okay, thank you for the heads up. I'll wait for @iluwatar to merge that PR, until I continue with that module.
It's merged now
Using JUnit 5 in Trampoline module. PR #1529 created.
I want to work on this issue if its is still unassigned .
Ok @sgarg5794 
I'm working on updating all the modules to JUnit5 right now. @ohbus 
Sure @charlesfinley you're good to go!
> Sure @charlesfinley you're good to go!

Sorry I opened a new PR for this - #1668 
@charlesfinley no need to be sorry for 😄  It is easier for us to review code in smaller chunks and this helps a lot 💯 

Thank you so much for your help!
> @charlesfinley no need to be sorry for 😄 It is easier for us to review code in smaller chunks and this helps a lot 💯
> 
> Thank you so much for your help!

This issue should probably be closed since the PR was merged and we have 2 new issues identifying the only modules lacking JUnit5. 
@charlesfinley great work!

We will close this issue for sure.
This issue has been partially addressed using #1542 and #1668 thanks to @charlesfinley 

Tests from two modules [**serverless**](https://github.com/iluwatar/java-design-patterns/tree/master/serverless) and [**naked-objects**](https://github.com/iluwatar/java-design-patterns/tree/master/naked-objects) are not being executed.

Respective issues have been opened for resolving the tests in favour of these two modules.
- [ ] #1667
- [ ] #1669

Resolving these two issues should close this [current issue](https://github.com/iluwatar/java-design-patterns/issues/1500)
@iluwatar let me know how can I help with this, I am confused about the status of the what has been resolved and what is pending.
Thanks for asking @anuragagarwal561994. I think the only remaining issue is that naked objects pattern still uses JUnit 4. Resolving this is covered by issues https://github.com/iluwatar/java-design-patterns/issues/1669 and https://github.com/iluwatar/java-design-patterns/issues/1683. I think we should close this task.

Pull request review comments:

Patch that resolved the issue:
diff --git a/pom.xml b/pom.xml
index 1275ce115c83..3b7ad35686b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,7 +52,7 @@
         <jaxb-api.version>2.3.1</jaxb-api.version>
         <jaxb-impl.version>2.3.2</jaxb-impl.version>
         <annotation-api.version>1.3.2</annotation-api.version>
-        <system-rules.version>1.19.0</system-rules.version>
+        <system-lambda.version>1.1.0</system-lambda.version>
         <urm.version>2.0.0</urm.version>
         <mockito-junit-jupiter.version>3.5.0</mockito-junit-jupiter.version>
         <!-- SonarCloud -->
@@ -338,8 +338,8 @@
             </dependency>
             <dependency>
                 <groupId>com.github.stefanbirkner</groupId>
-                <artifactId>system-rules</artifactId>
-                <version>${system-rules.version}</version>
+                <artifactId>system-lambda</artifactId>
+                <version>${system-lambda.version}</version>
                 <scope>test</scope>
             </dependency>
         </dependencies>
diff --git a/subclass-sandbox/pom.xml b/subclass-sandbox/pom.xml
index ac718291e2a4..a912ada3fdeb 100644
--- a/subclass-sandbox/pom.xml
+++ b/subclass-sandbox/pom.xml
@@ -42,7 +42,7 @@
         </dependency>
         <dependency>
             <groupId>com.github.stefanbirkner</groupId>
-            <artifactId>system-rules</artifactId>
+            <artifactId>system-lambda</artifactId>
         </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>


Test patch:
diff --git a/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/GroundDiveTest.java b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/GroundDiveTest.java
index 97e2ac67d16f..3b379946c6f0 100644
--- a/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/GroundDiveTest.java
+++ b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/GroundDiveTest.java
@@ -23,55 +23,48 @@
 
 package com.iluwatar.subclasssandbox;
 
+import com.github.stefanbirkner.systemlambda.Statement;
 import org.junit.Assert;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.contrib.java.lang.system.SystemOutRule;
+
+import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOutNormalized;
 
 /**
  * GroundDive unit tests.
  */
 public class GroundDiveTest {
 
-  @Rule
-  public SystemOutRule log = new SystemOutRule().enableLog();
-
   @Test
-  public void testMove() {
-    log.clearLog();
+  public void testMove() throws Exception {
     var groundDive = new GroundDive();
     groundDive.move(1.0, 1.0, 1.0);
-    var outputLog = getLogContent(log.getLog());
+    var outputLog = getLogContent(() -> groundDive.move(1.0, 1.0, 1.0));
     var expectedLog = "Move to ( 1.0, 1.0, 1.0 )";
     Assert.assertEquals(outputLog, expectedLog);
   }
 
   @Test
-  public void testPlaySound() {
-    log.clearLog();
+  public void testPlaySound() throws Exception {
     var groundDive = new GroundDive();
-    groundDive.playSound("SOUND_NAME", 1);
-    var outputLog = getLogContent(log.getLog());
+    var outputLog = getLogContent(() -> groundDive.playSound("SOUND_NAME", 1));
     var expectedLog = "Play SOUND_NAME with volumn 1";
     Assert.assertEquals(outputLog, expectedLog);
   }
 
   @Test
-  public void testSpawnParticles() {
-    log.clearLog();
+  public void testSpawnParticles() throws Exception {
     var groundDive = new GroundDive();
-    groundDive.spawnParticles("PARTICLE_TYPE", 100);
-    final var outputLog = getLogContent(log.getLog());
+    final var outputLog = getLogContent(
+            () -> groundDive.spawnParticles("PARTICLE_TYPE", 100));
     final var expectedLog = "Spawn 100 particle with type PARTICLE_TYPE";
     Assert.assertEquals(outputLog, expectedLog);
   }
 
   @Test
-  public void testActivate() {
-    log.clearLog();
+  public void testActivate() throws Exception {
     var groundDive = new GroundDive();
-    groundDive.activate();
-    var logs = log.getLog().split("\n");
+    var logs = tapSystemOutNormalized(groundDive::activate)
+            .split("\n");
     final var expectedSize = 3;
     final var log1 = logs[0].split("-")[1].trim() + " -" + logs[0].split("-")[2].trim();
     final var expectedLog1 = "Move to ( 0.0, 0.0, -20.0 )";
@@ -85,6 +78,11 @@ public void testActivate() {
     Assert.assertEquals(log3, expectedLog3);
   }
 
+  private String getLogContent(Statement statement) throws Exception {
+    var log = tapSystemOutNormalized(statement);
+    return getLogContent(log);
+  }
+
   private String getLogContent(String log) {
     return log.split("-")[1].trim();
   }
diff --git a/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/SkyLaunchTest.java b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/SkyLaunchTest.java
index e192737f6320..d285e6c7ddb2 100644
--- a/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/SkyLaunchTest.java
+++ b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/SkyLaunchTest.java
@@ -23,55 +23,47 @@
 
 package com.iluwatar.subclasssandbox;
 
+import com.github.stefanbirkner.systemlambda.Statement;
 import org.junit.Assert;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.contrib.java.lang.system.SystemOutRule;
+
+import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOutNormalized;
 
 /**
  * SkyLaunch unit tests.
  */
 public class SkyLaunchTest {
 
-  @Rule
-  public SystemOutRule log = new SystemOutRule().enableLog();
-
   @Test
-  public void testMove() {
-    log.clearLog();
+  public void testMove() throws Exception {
     var skyLaunch = new SkyLaunch();
-    skyLaunch.move(1.0, 1.0, 1.0);
-    var outputLog = getLogContent(log.getLog());
+    var outputLog = getLogContent(() -> skyLaunch.move(1.0, 1.0, 1.0));
     var expectedLog = "Move to ( 1.0, 1.0, 1.0 )";
     Assert.assertEquals(outputLog, expectedLog);
   }
 
   @Test
-  public void testPlaySound() {
-    log.clearLog();
+  public void testPlaySound() throws Exception {
     var skyLaunch = new SkyLaunch();
-    skyLaunch.playSound("SOUND_NAME", 1);
-    var outputLog = getLogContent(log.getLog());
+    var outputLog = getLogContent(() -> skyLaunch.playSound("SOUND_NAME", 1));
     var expectedLog = "Play SOUND_NAME with volumn 1";
     Assert.assertEquals(outputLog, expectedLog);
   }
 
   @Test
-  public void testSpawnParticles() {
-    log.clearLog();
+  public void testSpawnParticles() throws Exception {
     var skyLaunch = new SkyLaunch();
-    skyLaunch.spawnParticles("PARTICLE_TYPE", 100);
-    var outputLog = getLogContent(log.getLog());
+    var outputLog = getLogContent(
+            () -> skyLaunch.spawnParticles("PARTICLE_TYPE", 100));
     var expectedLog = "Spawn 100 particle with type PARTICLE_TYPE";
     Assert.assertEquals(outputLog, expectedLog);
   }
 
   @Test
-  public void testActivate() {
-    log.clearLog();
+  public void testActivate() throws Exception {
     var skyLaunch = new SkyLaunch();
-    skyLaunch.activate();
-    var logs = log.getLog().split("\n");
+    var logs = tapSystemOutNormalized(skyLaunch::activate)
+            .split("\n");
     final var expectedSize = 3;
     final var log1 = getLogContent(logs[0]);
     final var expectedLog1 = "Move to ( 0.0, 0.0, 20.0 )";
@@ -85,6 +77,11 @@ public void testActivate() {
     Assert.assertEquals(log3, expectedLog3);
   }
 
+  private String getLogContent(Statement statement) throws Exception {
+    var log = tapSystemOutNormalized(statement);
+    return getLogContent(log);
+  }
+
   private String getLogContent(String log) {
     return log.split("-")[1].trim();
   }