From 3e96da889cba00411d5175cc7548a4ff31c4bac1 Mon Sep 17 00:00:00 2001 From: Artem Ptushkin Date: Wed, 19 Jun 2019 11:16:41 +0300 Subject: [PATCH] Fixed MissingMethodExceptionNoStack at creating assertion for spock test (#1110) * Created test for the issue #1089 - failed with MissingMethodExceptionNoStack * Created groovy unit test for the issue #1089 - failed with MissingMethodExceptionNoStack * Fixed MissingMethodExceptionNoStack (#1089) * Changed SpringCloudContractAssertions to groovy with compileStatic fixes gh-1089 --- .../main/java/hello/GreetingController.java | 24 +- .../src/main/java/hello/GreetingResponse.java | 47 +++ .../contracts/greetings_post_ok.groovy | 53 +-- .../SpringCloudContractAssertions.groovy | 19 + .../assertion/CollectionAssertSpec.groovy | 360 ++++++++++++++++++ 5 files changed, 475 insertions(+), 28 deletions(-) create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/main/java/hello/GreetingResponse.java rename spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/assertion/SpringCloudContractAssertions.java => spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/test/resources/contracts/greetings_post_ok.groovy (50%) create mode 100644 spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/assertion/SpringCloudContractAssertions.groovy create mode 100644 spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/assertion/CollectionAssertSpec.groovy diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/main/java/hello/GreetingController.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/main/java/hello/GreetingController.java index ac5feb3e39..a6bb8747a2 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/main/java/hello/GreetingController.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/main/java/hello/GreetingController.java @@ -14,12 +14,15 @@ * limitations under the License. */package hello; -import java.util.concurrent.atomic.AtomicLong; - -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicLong; + +import static java.util.Arrays.asList; + @RestController public class GreetingController { @@ -27,10 +30,23 @@ public class GreetingController { private final AtomicLong counter = new AtomicLong(); - @RequestMapping("/greeting") + @GetMapping("/greeting") public Greeting greeting( @RequestParam(value = "name", defaultValue = "World") String name) { return new Greeting(counter.incrementAndGet(), String.format(template, name)); } + @GetMapping(value = "/accounts") + public GreetingResponse accounts( + @RequestParam(value = "name", defaultValue = "World") String name) { + return new GreetingResponse( + name, + new ArrayList<>( + asList( + new GreetingResponse.Account("account1"), + new GreetingResponse.Account("account2")) + ) + ); + } + } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/main/java/hello/GreetingResponse.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/main/java/hello/GreetingResponse.java new file mode 100644 index 0000000000..5510787a85 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/main/java/hello/GreetingResponse.java @@ -0,0 +1,47 @@ +package hello; + +import java.util.List; + +public class GreetingResponse { + String name; + List accounts; + + public GreetingResponse(String name, List accounts) { + this.name = name; + this.accounts = accounts; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getAccounts() { + return accounts; + } + + public void setAccounts(List accounts) { + this.accounts = accounts; + } + + static class Account { + + String key; + + public Account(String key) { + this.key = key; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + } +} diff --git a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/assertion/SpringCloudContractAssertions.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/test/resources/contracts/greetings_post_ok.groovy similarity index 50% rename from spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/assertion/SpringCloudContractAssertions.java rename to spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/test/resources/contracts/greetings_post_ok.groovy index cd4cf90f32..ff871765c0 100644 --- a/spring-cloud-contract-verifier/src/main/java/org/springframework/cloud/contract/verifier/assertion/SpringCloudContractAssertions.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/src/test/resources/contracts/greetings_post_ok.groovy @@ -12,29 +12,34 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.assertion; - -import org.assertj.core.api.Assertions; - -/** - * Assertions used by the generated tests. - * - * @author Marcin Grzejszczak - * @since 1.1.0 - */ -public class SpringCloudContractAssertions extends Assertions { - - /** - * Creates a new instance of {@link CollectionAssert}. - * @param type to assert - * @param actual the actual value. - * @return the created assertion object. - */ - public static CollectionAssert assertThat( - Iterable actual) { - return new CollectionAssert<>(actual); + */ org.springframework.cloud.contract.spec.Contract.make { + priority 2 + request { + method 'GET' + urlPath('/accounts') { + queryParameters { + parameter 'name': 'Something' + } + } + headers { + header 'Content-Type': 'application/json' + } + } + response { + status OK() + body ( + name: fromRequest().query('name'), + accounts: [ + [ + key: 'account1' + ], + [ + key: 'account2' + ] + ] + ) + bodyMatchers { + jsonPath('$.accounts[*].key', byRegex('account.*')) + } } - } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/assertion/SpringCloudContractAssertions.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/assertion/SpringCloudContractAssertions.groovy new file mode 100644 index 0000000000..0f25773135 --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/assertion/SpringCloudContractAssertions.groovy @@ -0,0 +1,19 @@ +package org.springframework.cloud.contract.verifier.assertion + +import groovy.transform.CompileStatic +import org.assertj.core.api.Assertions + +@CompileStatic +public class SpringCloudContractAssertions extends Assertions { + + /** + * Creates a new instance of {@link CollectionAssert}. + * @param type to assert + * @param actual the actual value. + * @return the created assertion object. + */ + public static CollectionAssert assertThat( + Iterable actual) { + return new CollectionAssert<>(actual); + } +} diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/assertion/CollectionAssertSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/assertion/CollectionAssertSpec.groovy new file mode 100644 index 0000000000..69b244b978 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/assertion/CollectionAssertSpec.groovy @@ -0,0 +1,360 @@ +package org.springframework.cloud.contract.verifier.assertion + +import org.assertj.core.api.Assertions +import spock.lang.Specification + +/** + * @author Marcin Grzejszczak, Artem Ptushkin + */ +class CollectionAssertSpec extends Specification { + + def should_not_throw_an_exception_when_all_elements_match_regex() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection).allElementsMatch("[a-z]") + + then: + noExceptionThrown() + } + + def should_throw_an_exception_when_at_least_one_element_doesnt_match_regex() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .allElementsMatch("[0-9]") + + then: + AssertionError e = thrown() + Assertions.assertThat(e).hasMessageContaining( + "The value doesn't match the regex <[0-9]>") + } + + def should_throw_an_exception_when_element_is_null() { + setup: + Collection collection = collectionWithNulls() + + when: + SpringCloudContractAssertions.assertThat(collection) + .allElementsMatch("[0-9]") + + then: + AssertionError e = thrown() + Assertions.assertThat(e).hasMessageContaining( + "The value doesn't match the regex <[0-9]>") + + } + + def should_throw_an_exception_when_collection_is_null() { + setup: + Collection collection = null + + when: + SpringCloudContractAssertions.assertThat(collection).allElementsMatch("foo") + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("Expecting actual not to be null") + } + + def should_throw_an_exception_when_collection_is_empty() { + setup: + Collection collection = new ArrayList() + + when: + SpringCloudContractAssertions.assertThat(collection).allElementsMatch("foo") + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("Expecting actual not to be empty") + } + + def should_not_throw_an_exception_when_flattened_size_is_greater_than_or_equal_to_provided_size() { + setup: + Collection collection = nestedCollection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasFlattenedSizeGreaterThanOrEqualTo(0) + .hasFlattenedSizeGreaterThanOrEqualTo(4) + + then: + noExceptionThrown() + } + + def should_throw_an_exception_when_flattened_size_is_not_greater_than_or_equal_to_provided_size() { + setup: + Collection collection = nestedCollection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasFlattenedSizeGreaterThanOrEqualTo(5) + + then: + AssertionError e = thrown() + Assertions.assertThat(e).hasMessageContaining( + "The flattened size <4> is not greater or equal to <5>") + } + + def should_throw_an_exception_when_collection_is_null_for_flattened_greater_than_or_equal() { + setup: + Collection collection = null + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasFlattenedSizeGreaterThanOrEqualTo(1) + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("Expecting actual not to be null") + } + + def should_not_throw_an_exception_when_flattened_size_is_less_than_or_equal_to_provided_size() { + setup: + Collection collection = nestedCollection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasFlattenedSizeLessThanOrEqualTo(5) + .hasFlattenedSizeLessThanOrEqualTo(4) + + then: + noExceptionThrown() + } + + def should_throw_an_exception_when_flattened_size_is_not_less_than_or_equal_to_provided_size() { + setup: + Collection collection = nestedCollection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasFlattenedSizeLessThanOrEqualTo(1) + + then: + AssertionError e = thrown() + Assertions.assertThat(e).hasMessageContaining( + "The flattened size <4> is not less or equal to <1>") + } + + def should_throw_an_exception_when_collection_is_null_for_flattened_less_than_or_equal() { + setup: + Collection collection = null + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasFlattenedSizeLessThanOrEqualTo(1) + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("Expecting actual not to be null") + } + + def should_not_throw_an_exception_when_flattened_size_is_between_the_provided_sizes() { + setup: + Collection collection = nestedCollection() + + when: + SpringCloudContractAssertions.assertThat(collection).hasFlattenedSizeBetween(1, 5) + .hasFlattenedSizeBetween(4, 4) + + then: + noExceptionThrown() + } + + def should_throw_an_exception_when_flattened_size_is_not_between_the_provided_sizes() { + setup: + Collection collection = nestedCollection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasFlattenedSizeBetween(5, 7) + + then: + AssertionError e = thrown() + Assertions.assertThat(e).hasMessageContaining( + "The flattened size <4> is not between <5> and <7>") + } + + def should_throw_an_exception_when_collection_is_null_for_flattened_between() { + setup: + Collection collection = null + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasFlattenedSizeBetween(1, 2) + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("Expecting actual not to be null") + } + + def should_not_throw_an_exception_when_size_is_greater_than_or_equal_to_provided_size() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasSizeGreaterThanOrEqualTo(0).hasSizeGreaterThanOrEqualTo(3) + + then: + noExceptionThrown() + } + + def should_throw_an_exception_when_size_is_not_greater_than_or_equal_to_provided_size() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasSizeGreaterThanOrEqualTo(5) + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("The size <3> is not greater or equal to <5>") + } + + def should_throw_an_exception_when_collection_is_null_for_greater_than_or_equal() { + setup: + Collection collection = null + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasSizeGreaterThanOrEqualTo(1) + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("Expecting actual not to be null") + } + + def should_not_throw_an_exception_when_size_is_less_than_or_equal_to_provided_size() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection).hasSizeLessThanOrEqualTo(4) + .hasSizeLessThanOrEqualTo(3) + + then: + noExceptionThrown() + } + + def should_throw_an_exception_when_size_is_not_less_than_or_equal_to_provided_size() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasSizeLessThanOrEqualTo(1) + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("The size <3> is not less or equal to <1>") + } + + def should_throw_an_exception_when_collection_is_null_for_less_than_or_equal() { + setup: + Collection collection = null + + when: + SpringCloudContractAssertions.assertThat(collection) + .hasSizeLessThanOrEqualTo(1) + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("Expecting actual not to be null") + } + + def should_not_throw_an_exception_when_size_is_between_the_provided_sizes() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection).hasSizeBetween(1, 4) + .hasSizeBetween(3, 3) + + then: + noExceptionThrown() + } + + def should_throw_an_exception_when_size_is_not_between_the_provided_sizes() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection).hasSizeBetween(5, 7) + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("The size <3> is not between <5> and <7>") + } + + def should_not_break_compilation_when_using_as() { + setup: + Collection collection = collection() + + when: + SpringCloudContractAssertions.assertThat(collection).as("for jsonpath x.y.z") + .hasSizeBetween(5, 7) + + then: + AssertionError e = thrown() + Assertions.assertThat(e).hasMessageContaining( + "[for jsonpath x.y.z] The size <3> is not between <5> and <7>") + } + + def should_throw_an_exception_when_collection_is_null_for_between() { + setup: + Collection collection = null + + when: + SpringCloudContractAssertions.assertThat(collection).hasSizeBetween( 1, 2) + + then: + AssertionError e = thrown() + Assertions.assertThat(e) + .hasMessageContaining("Expecting actual not to be null") + } + + Collection collection() { + List list = new ArrayList<>() + list.add("a") + list.add("b") + list.add("c") + return list + } + + Collection collectionWithNulls() { + List list = new ArrayList<>() + list.add(null) + return list + } + + Collection nestedCollection() { + List list = new ArrayList<>() + List list1 = new ArrayList<>() + Map map1 = new HashMap<>() + map1.put("a", "1") + map1.put("b", "2") + map1.put("c", "3") + List list2 = new ArrayList<>() + Map map2 = new HashMap<>() + map2.put("d", "4") + list.add(list1) + list.add(list2) + list1.add(map1) + list2.add(map2) + return list + } +} +