From 01ab66f84fe328645b7b4bdcec7e6f95238aff72 Mon Sep 17 00:00:00 2001 From: Kacper Koza Date: Sun, 3 Mar 2019 00:11:21 +0100 Subject: [PATCH 1/4] Spy, mock, stub in Spock Framework --- testing-modules/groovy-spock/pom.xml | 4 +- .../main/java/mocks/CharacterCalculator.java | 15 ++ .../src/main/java/mocks/EventPublisher.java | 7 + .../mocks/ExternalItemProviderException.java | 5 + .../src/main/java/mocks/Item.java | 36 +++++ .../src/main/java/mocks/ItemProvider.java | 9 ++ .../src/main/java/mocks/ItemService.java | 37 +++++ .../java/mocks/LoggingEventPublisher.java | 10 ++ .../test/groovy/mocks/ExampleSpockTest.groovy | 26 ++++ .../test/groovy/mocks/ItemServiceTest.groovy | 131 ++++++++++++++++++ 10 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 testing-modules/groovy-spock/src/main/java/mocks/CharacterCalculator.java create mode 100644 testing-modules/groovy-spock/src/main/java/mocks/EventPublisher.java create mode 100644 testing-modules/groovy-spock/src/main/java/mocks/ExternalItemProviderException.java create mode 100644 testing-modules/groovy-spock/src/main/java/mocks/Item.java create mode 100644 testing-modules/groovy-spock/src/main/java/mocks/ItemProvider.java create mode 100644 testing-modules/groovy-spock/src/main/java/mocks/ItemService.java create mode 100644 testing-modules/groovy-spock/src/main/java/mocks/LoggingEventPublisher.java create mode 100644 testing-modules/groovy-spock/src/test/groovy/mocks/ExampleSpockTest.groovy create mode 100644 testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceTest.groovy diff --git a/testing-modules/groovy-spock/pom.xml b/testing-modules/groovy-spock/pom.xml index 3dd01c29ab..77b6fbeb28 100644 --- a/testing-modules/groovy-spock/pom.xml +++ b/testing-modules/groovy-spock/pom.xml @@ -48,9 +48,9 @@ - 1.0-groovy-2.4 + 1.3-RC1-groovy-2.4 2.4.7 1.5 - \ No newline at end of file + diff --git a/testing-modules/groovy-spock/src/main/java/mocks/CharacterCalculator.java b/testing-modules/groovy-spock/src/main/java/mocks/CharacterCalculator.java new file mode 100644 index 0000000000..92a0cabdcb --- /dev/null +++ b/testing-modules/groovy-spock/src/main/java/mocks/CharacterCalculator.java @@ -0,0 +1,15 @@ +package mocks; + +public class CharacterCalculator { + + public int countCharacterInString(String value, char characterToCount) { + int result = 0; + for (int i = 0; i < value.length(); i++) { + if (value.charAt(i) == characterToCount) { + result++; + } + } + return result; + } + +} diff --git a/testing-modules/groovy-spock/src/main/java/mocks/EventPublisher.java b/testing-modules/groovy-spock/src/main/java/mocks/EventPublisher.java new file mode 100644 index 0000000000..290966db9f --- /dev/null +++ b/testing-modules/groovy-spock/src/main/java/mocks/EventPublisher.java @@ -0,0 +1,7 @@ +package mocks; + +public interface EventPublisher { + + void publish(String addedOfferId); + +} diff --git a/testing-modules/groovy-spock/src/main/java/mocks/ExternalItemProviderException.java b/testing-modules/groovy-spock/src/main/java/mocks/ExternalItemProviderException.java new file mode 100644 index 0000000000..e2ac43b7f4 --- /dev/null +++ b/testing-modules/groovy-spock/src/main/java/mocks/ExternalItemProviderException.java @@ -0,0 +1,5 @@ +package mocks; + +public class ExternalItemProviderException extends RuntimeException { + +} diff --git a/testing-modules/groovy-spock/src/main/java/mocks/Item.java b/testing-modules/groovy-spock/src/main/java/mocks/Item.java new file mode 100644 index 0000000000..e1608cfd23 --- /dev/null +++ b/testing-modules/groovy-spock/src/main/java/mocks/Item.java @@ -0,0 +1,36 @@ +package mocks; + +import java.util.Objects; + +public class Item { + private final String id; + private final String name; + + public Item(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Item item = (Item) o; + return Objects.equals(id, item.id) && + Objects.equals(name, item.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + +} diff --git a/testing-modules/groovy-spock/src/main/java/mocks/ItemProvider.java b/testing-modules/groovy-spock/src/main/java/mocks/ItemProvider.java new file mode 100644 index 0000000000..a794c51f49 --- /dev/null +++ b/testing-modules/groovy-spock/src/main/java/mocks/ItemProvider.java @@ -0,0 +1,9 @@ +package mocks; + +import java.util.List; + +public interface ItemProvider { + + List getItems(List itemIds); + +} diff --git a/testing-modules/groovy-spock/src/main/java/mocks/ItemService.java b/testing-modules/groovy-spock/src/main/java/mocks/ItemService.java new file mode 100644 index 0000000000..3327a8b27c --- /dev/null +++ b/testing-modules/groovy-spock/src/main/java/mocks/ItemService.java @@ -0,0 +1,37 @@ +package mocks; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +public class ItemService { + private final ItemProvider itemProvider; + private final EventPublisher eventPublisher; + + + public ItemService(ItemProvider itemProvider, EventPublisher eventPublisher) { + this.itemProvider = itemProvider; + this.eventPublisher = eventPublisher; + } + + List getAllItemsSortedByName(List itemIds) { + List items; + try{ + items = itemProvider.getItems(itemIds); + } catch (RuntimeException ex) { + throw new ExternalItemProviderException(); + } + return items.stream() + .sorted(Comparator.comparing(Item::getName)) + .collect(Collectors.toList()); + } + + void saveItems(List itemIds) { + List notEmptyOfferIds = itemIds.stream() + .filter(itemId -> !itemId.isEmpty()) + .collect(Collectors.toList()); + // save in database + notEmptyOfferIds.forEach(eventPublisher::publish); + } + +} diff --git a/testing-modules/groovy-spock/src/main/java/mocks/LoggingEventPublisher.java b/testing-modules/groovy-spock/src/main/java/mocks/LoggingEventPublisher.java new file mode 100644 index 0000000000..c51fe1ff77 --- /dev/null +++ b/testing-modules/groovy-spock/src/main/java/mocks/LoggingEventPublisher.java @@ -0,0 +1,10 @@ +package mocks; + +public class LoggingEventPublisher implements EventPublisher { + + @Override + public void publish(String addedOfferId) { + System.out.println("I've published: " + addedOfferId); + } + +} diff --git a/testing-modules/groovy-spock/src/test/groovy/mocks/ExampleSpockTest.groovy b/testing-modules/groovy-spock/src/test/groovy/mocks/ExampleSpockTest.groovy new file mode 100644 index 0000000000..3d424b0796 --- /dev/null +++ b/testing-modules/groovy-spock/src/test/groovy/mocks/ExampleSpockTest.groovy @@ -0,0 +1,26 @@ +package mocks + +import spock.lang.Specification + +class ExampleSpockTest extends Specification { + + CharacterCalculator characterCalculator + + def setup() { + characterCalculator = new CharacterCalculator() + } + + def 'should calculate character occurrences in given string'() { + given: + char characterToCount = 'o' + String value = 'Hello world!' + + when: + int result = characterCalculator.countCharacterInString(value, characterToCount) + + then: + result == 2 + } + + +} diff --git a/testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceTest.groovy b/testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceTest.groovy new file mode 100644 index 0000000000..2c7c4402e9 --- /dev/null +++ b/testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceTest.groovy @@ -0,0 +1,131 @@ +package mocks + +import spock.lang.Specification + +class ItemServiceTest extends Specification { + + ItemProvider itemProvider + ItemService itemService + EventPublisher eventPublisher + + def setup() { + itemProvider = Stub(ItemProvider) + eventPublisher = Mock(EventPublisher) + itemService = new ItemService(itemProvider, eventPublisher) + } + + def 'should return items sorted by name'() { + given: + def ids = ['offer-id', 'offer-id-2'] + itemProvider.getItems(ids) >> [new Item('offer-id-2', 'Zname'), new Item('offer-id', 'Aname')] + + when: + List items = itemService.getAllItemsSortedByName(ids) + + then: + items.collect { it.name } == ['Aname', 'Zname'] + } + + def 'arguments constraints'() { + itemProvider.getItems(['offer-id']) + itemProvider.getItems(_) >> [] + itemProvider.getItems(*_) >> [] + itemProvider.getItems(!null) >> [] + itemProvider.getItems({ it.size > 0 }) >> [] + } + + def 'should return different items on subsequent call'() { + given: + itemProvider.getItems(_) >>> [ + [], + [new Item('1', 'name')], + [new Item('2', 'name')] + ] + + when: 'method is called for the first time' + List items = itemService.getAllItemsSortedByName(['not-important']) + + then: 'empty list is returned' + items == [] + + when: 'method is called for the second time' + items = itemService.getAllItemsSortedByName(['not-important']) + + then: 'item with id=1 is returned' + items == [new Item('1', 'name')] + + when: 'method is called for the thirdtime' + items = itemService.getAllItemsSortedByName(['not-important']) + + then: 'item with id=2 is returned' + items == [new Item('2', 'name')] + } + + def 'should throw ExternalItemProviderException when ItemProvider fails'() { + given: + itemProvider.getItems(_) >> { new RuntimeException()} + + when: + itemService.getAllItemsSortedByName([]) + + then: + thrown(ExternalItemProviderException) + } + + def 'chaining response'() { + itemProvider.getItems(_) >>> { new RuntimeException() } >> new SocketTimeoutException() >> [new Item('id', 'name')] + } + + def 'should return different items for different ids lists'() { + given: + def firstIds = ['first'] + def secondIds = ['second'] + itemProvider.getItems(firstIds) >> [new Item('first', 'Zname')] + itemProvider.getItems(secondIds) >> [new Item('second', 'Aname')] + + when: + def firstItems = itemService.getAllItemsSortedByName(firstIds) + def secondItems = itemService.getAllItemsSortedByName(secondIds) + + then: + firstItems.first().name == 'Zname' + secondItems.first().name == 'Aname' + } + + def 'should publish events about new non-empty saved offers'() { + given: + def offerIds = ['', 'a', 'b'] + + when: + itemService.saveItems(offerIds) + + then: + 2 * eventPublisher.publish({ it != null && !it.isEmpty()}) + } + + def 'should return items'() { + given: + itemProvider = Mock(ItemProvider) + itemProvider.getItems(['item-id']) >> [new Item('item-id', 'name')] + itemService = new ItemService(itemProvider, eventPublisher) + + when: + def items = itemService.getAllItemsSortedByName(['item-id']) + + then: + items == [new Item('item-id', 'name')] + } + + def 'should spy on EventPublisher method call'() { + given: + LoggingEventPublisher eventPublisher = Spy(LoggingEventPublisher) + itemService = new ItemService(itemProvider, eventPublisher) + + when: + itemService.saveItems(['item-id']) + + then: + 1 * eventPublisher.publish('item-id') + } + +} From 24a3fa2ae700cc0c6a1a26b77fa828648c31f37c Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sun, 3 Mar 2019 09:16:10 +0200 Subject: [PATCH 2/4] Update and rename ItemServiceTest.groovy to ItemServiceUnitTest.groovy --- .../{ItemServiceTest.groovy => ItemServiceUnitTest.groovy} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename testing-modules/groovy-spock/src/test/groovy/mocks/{ItemServiceTest.groovy => ItemServiceUnitTest.groovy} (98%) diff --git a/testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceTest.groovy b/testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceUnitTest.groovy similarity index 98% rename from testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceTest.groovy rename to testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceUnitTest.groovy index 2c7c4402e9..2ccb72f726 100644 --- a/testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceTest.groovy +++ b/testing-modules/groovy-spock/src/test/groovy/mocks/ItemServiceUnitTest.groovy @@ -2,7 +2,7 @@ package mocks import spock.lang.Specification -class ItemServiceTest extends Specification { +class ItemServiceUnitTest extends Specification { ItemProvider itemProvider ItemService itemService From 9611f5a5cf3b8b072b007dc701c3ce31be6efb74 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sun, 3 Mar 2019 09:16:36 +0200 Subject: [PATCH 3/4] Delete ExampleSpockTest.groovy --- .../test/groovy/mocks/ExampleSpockTest.groovy | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 testing-modules/groovy-spock/src/test/groovy/mocks/ExampleSpockTest.groovy diff --git a/testing-modules/groovy-spock/src/test/groovy/mocks/ExampleSpockTest.groovy b/testing-modules/groovy-spock/src/test/groovy/mocks/ExampleSpockTest.groovy deleted file mode 100644 index 3d424b0796..0000000000 --- a/testing-modules/groovy-spock/src/test/groovy/mocks/ExampleSpockTest.groovy +++ /dev/null @@ -1,26 +0,0 @@ -package mocks - -import spock.lang.Specification - -class ExampleSpockTest extends Specification { - - CharacterCalculator characterCalculator - - def setup() { - characterCalculator = new CharacterCalculator() - } - - def 'should calculate character occurrences in given string'() { - given: - char characterToCount = 'o' - String value = 'Hello world!' - - when: - int result = characterCalculator.countCharacterInString(value, characterToCount) - - then: - result == 2 - } - - -} From 84c8c70dac1d1786471197e269cf1fe59ad5931a Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sun, 3 Mar 2019 09:16:43 +0200 Subject: [PATCH 4/4] Delete CharacterCalculator.java --- .../src/main/java/mocks/CharacterCalculator.java | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 testing-modules/groovy-spock/src/main/java/mocks/CharacterCalculator.java diff --git a/testing-modules/groovy-spock/src/main/java/mocks/CharacterCalculator.java b/testing-modules/groovy-spock/src/main/java/mocks/CharacterCalculator.java deleted file mode 100644 index 92a0cabdcb..0000000000 --- a/testing-modules/groovy-spock/src/main/java/mocks/CharacterCalculator.java +++ /dev/null @@ -1,15 +0,0 @@ -package mocks; - -public class CharacterCalculator { - - public int countCharacterInString(String value, char characterToCount) { - int result = 0; - for (int i = 0; i < value.length(); i++) { - if (value.charAt(i) == characterToCount) { - result++; - } - } - return result; - } - -}