From 7c95b2692e7df72afb68d6a01a086110225097f0 Mon Sep 17 00:00:00 2001 From: Robin Wilkins Date: Mon, 3 Aug 2020 17:30:44 +0100 Subject: [PATCH] Add spock data driven test example. Added: - dependency on groovy, and spock framework for testing. - Spock test for the GreetingController - Filter adding a trace-id header to http responses --- build.gradle | 3 ++ .../wilkins/showcase/ShowcaseApplication.java | 11 ++++ .../showcase/filters/TraceIdFilter.java | 26 ++++++++++ .../com/wilkins/showcase/service/Example.java | 10 ++++ .../showcase/service/ExampleService.java | 6 +++ .../showcase/service/GreatExampleService.java | 36 +++++++++++++ src/main/resources/application.yaml | 4 ++ .../controllers/GreetingControllerSpec.groovy | 43 ++++++++++++++++ .../service/GreatExampleServiceSpec.groovy | 50 +++++++++++++++++++ 9 files changed, 189 insertions(+) create mode 100644 src/main/java/com/wilkins/showcase/filters/TraceIdFilter.java create mode 100644 src/main/java/com/wilkins/showcase/service/Example.java create mode 100644 src/main/java/com/wilkins/showcase/service/ExampleService.java create mode 100644 src/main/java/com/wilkins/showcase/service/GreatExampleService.java create mode 100644 src/main/resources/application.yaml create mode 100644 src/test/groovy/com/wilkins/showcase/controllers/GreetingControllerSpec.groovy create mode 100644 src/test/groovy/com/wilkins/showcase/service/GreatExampleServiceSpec.groovy diff --git a/build.gradle b/build.gradle index d93539e..8d076c1 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'org.springframework.boot' version '3.0.1' id 'io.spring.dependency-management' version '1.1.0' id 'java' + id 'groovy' } targetCompatibility = '17' @@ -14,6 +15,8 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation('org.springframework.boot:spring-boot-starter-test') + testImplementation 'org.spockframework:spock-core:2.4-M1-groovy-4.0' + testImplementation 'org.spockframework:spock-spring:2.4-M1-groovy-4.0' } test { diff --git a/src/main/java/com/wilkins/showcase/ShowcaseApplication.java b/src/main/java/com/wilkins/showcase/ShowcaseApplication.java index e2e2586..90250dc 100644 --- a/src/main/java/com/wilkins/showcase/ShowcaseApplication.java +++ b/src/main/java/com/wilkins/showcase/ShowcaseApplication.java @@ -2,11 +2,22 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.context.annotation.Bean; +import java.util.UUID; +import java.util.function.Supplier; + +@ServletComponentScan @SpringBootApplication public class ShowcaseApplication { public static void main(String[] args) { SpringApplication.run(ShowcaseApplication.class, args); } + + @Bean + public Supplier randomStringSupplier() { + return () -> UUID.randomUUID().toString(); + } } diff --git a/src/main/java/com/wilkins/showcase/filters/TraceIdFilter.java b/src/main/java/com/wilkins/showcase/filters/TraceIdFilter.java new file mode 100644 index 0000000..3284f1b --- /dev/null +++ b/src/main/java/com/wilkins/showcase/filters/TraceIdFilter.java @@ -0,0 +1,26 @@ +package com.wilkins.showcase.filters; + +import com.wilkins.showcase.service.ExampleService; +import jakarta.servlet.*; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@WebFilter("/*") +public class TraceIdFilter implements Filter { + + private final ExampleService idService; + + public TraceIdFilter(ExampleService idService) { + this.idService = idService; + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + httpServletResponse.setHeader("x-trace-id", idService.findExampleId()); + chain.doFilter(request, response); + } +} diff --git a/src/main/java/com/wilkins/showcase/service/Example.java b/src/main/java/com/wilkins/showcase/service/Example.java new file mode 100644 index 0000000..69bed2c --- /dev/null +++ b/src/main/java/com/wilkins/showcase/service/Example.java @@ -0,0 +1,10 @@ +package com.wilkins.showcase.service; + +import java.util.List; + +public record Example ( + String key, + String value, + String id, + List parts) { +} diff --git a/src/main/java/com/wilkins/showcase/service/ExampleService.java b/src/main/java/com/wilkins/showcase/service/ExampleService.java new file mode 100644 index 0000000..9609821 --- /dev/null +++ b/src/main/java/com/wilkins/showcase/service/ExampleService.java @@ -0,0 +1,6 @@ +package com.wilkins.showcase.service; + +public interface ExampleService { + String findExampleId(); + Example createExample(); +} diff --git a/src/main/java/com/wilkins/showcase/service/GreatExampleService.java b/src/main/java/com/wilkins/showcase/service/GreatExampleService.java new file mode 100644 index 0000000..d2be284 --- /dev/null +++ b/src/main/java/com/wilkins/showcase/service/GreatExampleService.java @@ -0,0 +1,36 @@ +package com.wilkins.showcase.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.function.Supplier; + +@Service +public class GreatExampleService implements ExampleService { + + private final String prefix; + private final Supplier supplier; + private final String joiner; + + public GreatExampleService(@Value("${showcase.id-service.prefix}") String prefix, + Supplier supplier, + @Value("${showcase.id-service.joiner}") String joiner) { + this.prefix = prefix; + this.supplier = supplier; + this.joiner = joiner; + } + + @Override + public String findExampleId() { + return prefix + joiner + supplier.get(); + } + + @Override + public Example createExample() { + var exampleId = findExampleId(); + var suppliedExample = supplier.get(); + return new Example(prefix, suppliedExample, exampleId, List.of(prefix, suppliedExample, exampleId)); + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..eb3f7c6 --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,4 @@ +showcase: + id-service: + prefix: "showcase" + joiner: "::" diff --git a/src/test/groovy/com/wilkins/showcase/controllers/GreetingControllerSpec.groovy b/src/test/groovy/com/wilkins/showcase/controllers/GreetingControllerSpec.groovy new file mode 100644 index 0000000..0d85599 --- /dev/null +++ b/src/test/groovy/com/wilkins/showcase/controllers/GreetingControllerSpec.groovy @@ -0,0 +1,43 @@ +package com.wilkins.showcase.controllers + +import com.wilkins.showcase.service.ExampleService +import org.spockframework.spring.SpringBean +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import spock.lang.Specification +import spock.lang.Unroll + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class GreetingControllerSpec extends Specification { + + @Autowired + TestRestTemplate restTemplate; + + @SpringBean + ExampleService exampleService = Mock() + + @Unroll + def "should wish #providedName an appropriate greeting"() { + given: + exampleService.findExampleId() >> "mock-id" + + when: + def response= restTemplate + .getForEntity("/greeting?name={name}&salutation={salutation}", Greeting.class, providedName, providedSalutation) + + then: + response.statusCode.value() == 200 + response.headers.getFirst('x-trace-id') == 'mock-id' + with(response.body) { + salutation == providedSalutation + name == providedName + } + + where: + providedName | providedSalutation + 'world' | 'hello' + 'robin' | "good afternoon" + } + +} diff --git a/src/test/groovy/com/wilkins/showcase/service/GreatExampleServiceSpec.groovy b/src/test/groovy/com/wilkins/showcase/service/GreatExampleServiceSpec.groovy new file mode 100644 index 0000000..c9bcbca --- /dev/null +++ b/src/test/groovy/com/wilkins/showcase/service/GreatExampleServiceSpec.groovy @@ -0,0 +1,50 @@ +package com.wilkins.showcase.service + +import spock.lang.Specification +import spock.lang.Unroll + +import java.util.function.Supplier + +class GreatExampleServiceSpec extends Specification { + + Supplier stringSupplier = Mock() + + @Unroll + def "ID String is generated correctly when using the GreatExampleService"() { + given: + stringSupplier.get() >> suppliedString + def idService = new GreatExampleService(something, stringSupplier, joiner) + + expect: + expectedId == idService.findExampleId() + + where: + something | suppliedString | joiner || expectedId + "abc" | "123" | "::" || "abc::123" + "1" | "2" | "||" || "1||2" + } + + @Unroll + def "Example is generated correctly when using the key: #prefix and suppliedString: #suppliedString"() { + given: + stringSupplier.get() >> suppliedString + def exampleService = new GreatExampleService(prefix, stringSupplier, joiner) + + when: + def example = exampleService.createExample() + + then: + with(example) { + key == prefix + value == suppliedString + id == expectedId + parts == [prefix, suppliedString, expectedId] as List + } + + where: + prefix | suppliedString | joiner || expectedId + "abc" | "123" | "::" || "abc::123" + "1" | "2" | "//" || "1//2" + + } +}