Further refined the example
authorKai Moritz <kai@juplo.de>
Sun, 27 Sep 2020 09:23:03 +0000 (11:23 +0200)
committerKai Moritz <kai@juplo.de>
Sun, 27 Sep 2020 17:54:19 +0000 (19:54 +0200)
* Introduced an ExampleService
* The service checks the answer for
  The Ultimate Question Of Life, The Universe And Everything
* Requests without any answer are tolarated, so that the page with the
  question can be rendered
* Only numbers (Integer) are allowed as answer.
* Negative numbers are not allowed as answers: the service answers with an
  empty Optional.
* The view contains a bug, that results in a 503 for negative answers.

src/main/java/de/juplo/demo/ExampleController.java
src/main/java/de/juplo/demo/ExampleService.java [new file with mode: 0644]
src/main/resources/templates/a.html [deleted file]
src/main/resources/templates/b.html [deleted file]
src/main/resources/templates/view.html [new file with mode: 0644]
src/test/java/de/juplo/demo/ExceptionHandlingApplicationTests.java

index b6e75d8..e16b147 100644 (file)
@@ -11,6 +11,8 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseStatus;
 import org.thymeleaf.exceptions.TemplateInputException;
 
+import java.util.Optional;
+
 @Controller
 public class ExampleController
 {
@@ -18,14 +20,27 @@ public class ExampleController
             LoggerFactory.getLogger(ExampleController.class);
 
 
+    private final ExampleService service;
+
+
+    public ExampleController(ExampleService service)
+    {
+        this.service = service;
+    }
+
+
     @RequestMapping("/")
     public String controller(
-            @RequestParam(defaultValue = "a") String template,
+            @RequestParam(required = false) Integer answer,
             Model model
     )
     {
-        model.addAttribute("template", template);
-        return template;
+        Optional<Boolean> outcome =
+                answer == null ? null : service.checkAnswer(answer);
+
+        model.addAttribute("answer", answer);
+        model.addAttribute("outcome", outcome);
+        return "view";
     }
 
     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
diff --git a/src/main/java/de/juplo/demo/ExampleService.java b/src/main/java/de/juplo/demo/ExampleService.java
new file mode 100644 (file)
index 0000000..375e8f7
--- /dev/null
@@ -0,0 +1,21 @@
+package de.juplo.demo;
+
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component
+public class ExampleService
+{
+    public Optional<Boolean> checkAnswer(int answer)
+    {
+        if (answer < 0)
+            return Optional.empty();
+
+        if (answer == 42)
+            return Optional.of(true);
+        else
+            return Optional.of(false);
+    }
+}
diff --git a/src/main/resources/templates/a.html b/src/main/resources/templates/a.html
deleted file mode 100644 (file)
index 60de028..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html xmlns:th="http://www.thymeleaf.org">
-  <head>
-    <title>Testing Exception-Handling - Template A</title>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-  </head>
-  <body>
-    <h1>Template A</h1>
-    <div>
-      <p><strong th:text="'Serving with template ::' + ${template} + '::!'">TEXT</strong></p>
-      <p>
-        Type in <strong><code>a</code></strong> or <strong><code>b</code></strong>
-        for an existing template (no exception). Type in any other string for a
-        non-existent template: Thymeleaf will throw a
-        <strong><code>TemplateInputException</code></strong>!
-      </p>
-    </div>
-    <div>
-      <form action="#" th:action="@{/}" method="get">
-        <div>
-          <label for="path">Remote-Path to fetch:</label>
-          <input type="text" name="template" value="a" th:value="${template}"/>
-        </div>
-        <button type="submit">Submit</button>
-      </form>
-    </div>
-  </body>
-</html>
diff --git a/src/main/resources/templates/b.html b/src/main/resources/templates/b.html
deleted file mode 100644 (file)
index c89c69f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE HTML>
-<html xmlns:th="http://www.thymeleaf.org">
-  <head>
-    <title>Testing Exception-Handling - Template B</title>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-  </head>
-  <body>
-    <h1>Template B</h1>
-    <div>
-      <p><strong th:text="'Serving with template ::' + ${template} + '::!'">TEXT</strong></p>
-      <p>
-        Type in <strong><code>a</code></strong> or <strong><code>b</code></strong>
-        for an existing template (no exception). Type in any other string for a
-        non-existent template: Thymeleaf will throw a
-        <strong><code>TemplateInputException</code></strong>!
-      </p>
-    </div>
-    <div>
-      <form action="#" th:action="@{/}" method="get">
-        <div>
-          <label for="path">Remote-Path to fetch:</label>
-          <input type="text" name="template" value="a" th:value="${template}"/>
-        </div>
-        <button type="submit">Submit</button>
-      </form>
-    </div>
-  </body>
-</html>
diff --git a/src/main/resources/templates/view.html b/src/main/resources/templates/view.html
new file mode 100644 (file)
index 0000000..47fdca4
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html xmlns:th="http://www.thymeleaf.org">
+  <head>
+    <title>Template: view</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  </head>
+  <body>
+    <h1>Deeep Thought</h1>
+    <div th:switch="${answer}">
+      <p th:case="null">
+        <strong>What is the answer to the ultimate question of life, the universe and everything?</strong>
+      </p>
+      <ul th:case="*">
+        <li>Presented answer: <strong th:text="${answer}">ANSWER</strong></li>
+        <li>Outcome: <strong th:text="${outcome.get()}">OUTCOME</strong></li>
+      </ul>
+    </div>
+    <div>
+      <form action="#" th:action="@{/}" method="get">
+        <div>
+          <label for="answer">Answer:</label>
+          <input type="text" name="answer" value="42" th:value="${answer}"/>
+          <button type="submit">Submit</button>
+        </div>
+      </form>
+    </div>
+  </body>
+</html>
index 9e6d753..5d3ca21 100644 (file)
@@ -1,14 +1,23 @@
 package de.juplo.demo;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Mockito;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.test.web.servlet.MockMvc;
 
 import java.net.URI;
+import java.util.Optional;
 
+import static org.mockito.AdditionalMatchers.geq;
+import static org.mockito.AdditionalMatchers.lt;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
@@ -17,6 +26,9 @@ class ExceptionHandlingApplicationTests {
        private final static Logger LOG =
                        LoggerFactory.getLogger(ExceptionHandlingApplicationTests.class);
 
+       @MockBean
+       ExampleService service;
+
        @Autowired
        MockMvc mvc;
 
@@ -26,17 +38,33 @@ class ExceptionHandlingApplicationTests {
        }
 
        @Test
-       void test200() throws Exception {
+       void test200ForNoAnswer() throws Exception {
                mvc
-                               .perform(get(URI.create("http://FOO/?template=a")))
+                               .perform(get(URI.create("http://FOO/")))
                                .andExpect(status().isOk());
+
+               verify(service, times(0)).checkAnswer(anyInt());
+       }
+
+       @ParameterizedTest
+       @ValueSource(ints = { 0, 1, 2, 3, 4, 5, 41, 42, 43, 666, Integer.MAX_VALUE })
+       void test200ForPositiveAnswer(int number) throws Exception {
+               when(service.checkAnswer(eq(42))).thenReturn(Optional.of(true));
+               when(service.checkAnswer(geq(0))).thenReturn(Optional.of(false));
+               when(service.checkAnswer(lt(0))).thenReturn(Optional.empty());
+
                mvc
-                               .perform(get(URI.create("http://FOO/?template=b")))
+                               .perform(get(URI.create("http://FOO/?answer=" + number)))
                                .andExpect(status().isOk());
        }
 
-       @Test
-       void test503_NOT_WORKING() throws Exception {
+       @ParameterizedTest
+       @ValueSource(ints = { -1, -2, Integer.MIN_VALUE })
+       void test500ForNegativeAnswer_NOT_WORKING(int number) throws Exception {
+               when(service.checkAnswer(eq(42))).thenReturn(Optional.of(true));
+               when(service.checkAnswer(geq(0))).thenReturn(Optional.of(false));
+               when(service.checkAnswer(lt(0))).thenReturn(Optional.empty());
+
                // The expected behaviour of the following test is, that the
                // TemplateInputException, that is thrown by Thymeleaf because of the non-existent
                // template-resource, is catched and reported as 503 Internal Server Error, as it
@@ -45,7 +73,18 @@ class ExceptionHandlingApplicationTests {
                // Instead, the exception bubbles up, becomes wrapped in a NestedServletException
                // and is thrown in the call to perform()!
                mvc
-                               .perform(get(URI.create("http://FOO/?template=foo")))
+                               .perform(get(URI.create("http://FOO/?answer=" + number)))
                                .andExpect(status().isInternalServerError());
        }
+
+       @Test
+       void test400ForStringInput() throws Exception {
+               when(service.checkAnswer(eq(42))).thenReturn(Optional.of(true));
+               when(service.checkAnswer(geq(0))).thenReturn(Optional.of(false));
+               when(service.checkAnswer(lt(0))).thenReturn(Optional.empty());
+
+               mvc
+                               .perform(get(URI.create("http://FOO/?answer=bar")))
+                               .andExpect(status().isBadRequest());
+       }
 }