From 31c3825b50f37ff6d3102c3d67ab79a7980d520d Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 11 Jan 2024 12:59:14 +0100 Subject: [PATCH 1/9] Added PrintableScenario class for creating BDD scenarios with Approvals. --- .../bdd/PrintableScenarioTest.java | 101 ++++++++++++++++++ ...ScenarioTest.printBDDScenario.approved.txt | 38 +++++++ approvaltests/docs/how_to/BddScenarios.md | 27 +++++ .../strings/PrintableScenario.java | 71 ++++++++++++ 4 files changed, 237 insertions(+) create mode 100644 approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java create mode 100644 approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt create mode 100644 approvaltests/docs/how_to/BddScenarios.md create mode 100644 approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java new file mode 100644 index 000000000..693c37ec8 --- /dev/null +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java @@ -0,0 +1,101 @@ +package org.approvaltests.bdd; + +import org.approvaltests.Approvals; +import org.approvaltests.strings.Printable; +import org.approvaltests.strings.PrintableScenario; +import org.junit.Test; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class PrintableScenarioTest { + + @Test + public void printBDDScenario() throws Exception { + PrintableScenario story = new PrintableScenario("Buy Groceries", "Bob puts two items in the shopping cart"); + + User currentUser = new User(); + ShoppingCart shoppingCart = new ShoppingCart(); + story.given( + new Printable<>(currentUser, UserPrinter::print), + new Printable<>(shoppingCart, ShoppingCartPrinter::print) + ); + + story.when("Add oranges to the cart", () -> { + shoppingCart.add("Oranges", 1, BigDecimal.valueOf(5)); + return null; + }); + + story.when("Add apples to the cart", () -> { + shoppingCart.add("Apples", 3, BigDecimal.valueOf(4)); + return null; + }); + + Approvals.verify(story.then()); + } + + +} + +class User { + public String name = "Bob"; +} + +class Item { + public String name; + public Integer quantity; + public BigDecimal price; + + public BigDecimal amount() { + return price.multiply(BigDecimal.valueOf(quantity)); + } + public Item(String itemName, int quantity, BigDecimal price) { + this.name = itemName; + this.quantity = quantity; + this.price = price; + } +} +class ShoppingCart { + public List articles = new ArrayList<>(); + public BigDecimal subtotal() { + return articles.stream().map(Item::amount).reduce(BigDecimal.ZERO, BigDecimal::add); + } + + public BigDecimal shipping() { + return BigDecimal.ONE; + } + + public BigDecimal total() { + return this.subtotal().add(this.shipping()); + } + + public void add(String itemName, int quantity, BigDecimal price) { + this.articles.add(new Item(itemName, quantity, price)); + } +} + +class ShoppingCartPrinter { + + public static String print(ShoppingCart shoppingCart) { + StringBuilder result = new StringBuilder(); + result.append("ShoppingCart:\n"); + result.append("Articles:\n"); + result.append(shoppingCart.articles.stream() + .map(article -> " " + article.name + " price: " + article.price + " quantity: " + article.quantity) + .collect(Collectors.joining("\n"))); + result.append("\n"); + + result.append("Subtotal:" + shoppingCart.subtotal() + "\n"); + result.append("Shipping:" + shoppingCart.shipping() + "\n"); + result.append("Total:" + shoppingCart.total() + "\n"); + return result.toString(); + } +} + +class UserPrinter { + public static String print(User user) { + return "User: " + user.name + "\n"; + } +} \ No newline at end of file diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt new file mode 100644 index 000000000..db5a989c7 --- /dev/null +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt @@ -0,0 +1,38 @@ +Buy Groceries +============= +Bob puts two items in the shopping cart + +User: Bob + +ShoppingCart: +Articles: + +Subtotal:0 +Shipping:1 +Total:1 + +Add oranges to the cart +----------------------- + +User: Bob + +ShoppingCart: +Articles: + Oranges price: 5 quantity: 1 +Subtotal:5 +Shipping:1 +Total:6 + +Add apples to the cart +---------------------- + +User: Bob + +ShoppingCart: +Articles: + Oranges price: 5 quantity: 1 + Apples price: 4 quantity: 3 +Subtotal:17 +Shipping:1 +Total:18 + diff --git a/approvaltests/docs/how_to/BddScenarios.md b/approvaltests/docs/how_to/BddScenarios.md new file mode 100644 index 000000000..2236c1fe1 --- /dev/null +++ b/approvaltests/docs/how_to/BddScenarios.md @@ -0,0 +1,27 @@ + + +# How to make a BDD Scenario using Approvals + +## Contents + +* [Introduction](#introduction) +* [Sample Code](#sample-code) + +## Introduction +The idea is that you will end up with a descriptive approved file that reads like a BDD Scenario. You can show this to your less-technical collaborators, and they should be able to understand what scenario is being tested without needing to read the test sourcecode. + +It doesn't use Gherkin strictly, but you still get a scenario with a name, description, and Given, When, Then steps. + +## Sample Code + +I need some help with this part of the documentation. I'd like to include PrintableScenarioTest here as an example. + + + + +* Construct your scenario at the beginning of the test with a descriptive name and summary. +* When you are done with the code for the "given" step of the test, call story.given() with your list of Printables. Use one printable to wrap each object you have created which you think might change state during the test and that you want to verify. +* In each "When" step of your test, call when() with a descriptive name for what the user is doing. The second argument is a lambda that contains code to actually do the when step. This is optional, you can write it in your test as normal code before the call to 'when' if you prefer. +* The "Then" step is a call to then() which will verify the state of all the Printables as it changed throughout the course of the test. + +Note - if you don't want to use the BDD terminology you don't have to. You can equally well use 'arrange', 'act', 'print' instead of 'given' 'when' 'then' diff --git a/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java b/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java new file mode 100644 index 000000000..7e5beb1d6 --- /dev/null +++ b/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java @@ -0,0 +1,71 @@ +package org.approvaltests.strings; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.Callable; + +public class PrintableScenario { + + + private final StringBuilder toVerify = new StringBuilder(); + private final String name; + private final String description; + private final ArrayList printables = new ArrayList<>(); + + public PrintableScenario(String name, String description) { + this.name = name; + this.description = description; + } + public PrintableScenario(String name) { + this(name, ""); + } + + public void given(Printable... printables) { + arrange(printables); + } + + public void arrange(Printable... printables) { + this.printables.addAll(Arrays.asList(printables)); + toVerify.append(makeHeading(name, description, "=")); + toVerify.append("\n"); + toVerify.append(printAll()); + } + + public String makeHeading(String heading, String summary, String underlineCharacter) { + int count = heading.length(); + // create a string made up of n copies of string "=" + String underlineHeading = String.join("", Collections.nCopies(count, underlineCharacter)); + return heading + "\n" + underlineHeading + "\n" + summary + "\n"; + } + + public String printAll() { + StringBuilder result = new StringBuilder(); + for (Printable printable : this.printables) { + result.append(printable.toString()); + result.append("\n"); + } + return result.toString(); + } + + public void when(String action, Callable function) throws Exception { + function.call(); + when(action); + } + + public void when(String action) { + act(action); + } + + public void act(String action) { + toVerify.append(makeHeading(action, "", "-")); + toVerify.append(printAll()); + } + public String then() { + return print(); + } + + public String print() { + return toVerify.toString(); + } +} From 942669765c556fd7a43fd66f08edc83ff251b2ac Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 11 Jan 2024 12:00:46 +0000 Subject: [PATCH 2/9] a reformat code --- .../bdd/PrintableScenarioTest.java | 157 +++++++++--------- .../strings/PrintableScenario.java | 126 +++++++------- 2 files changed, 143 insertions(+), 140 deletions(-) diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java index 693c37ec8..eaa483c63 100644 --- a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java @@ -10,92 +10,93 @@ import java.util.List; import java.util.stream.Collectors; -public class PrintableScenarioTest { - - @Test - public void printBDDScenario() throws Exception { - PrintableScenario story = new PrintableScenario("Buy Groceries", "Bob puts two items in the shopping cart"); - - User currentUser = new User(); - ShoppingCart shoppingCart = new ShoppingCart(); - story.given( - new Printable<>(currentUser, UserPrinter::print), - new Printable<>(shoppingCart, ShoppingCartPrinter::print) - ); - - story.when("Add oranges to the cart", () -> { - shoppingCart.add("Oranges", 1, BigDecimal.valueOf(5)); - return null; - }); - - story.when("Add apples to the cart", () -> { - shoppingCart.add("Apples", 3, BigDecimal.valueOf(4)); - return null; - }); - - Approvals.verify(story.then()); - } - - +public class PrintableScenarioTest +{ + @Test + public void printBDDScenario() throws Exception + { + PrintableScenario story = new PrintableScenario("Buy Groceries", "Bob puts two items in the shopping cart"); + User currentUser = new User(); + ShoppingCart shoppingCart = new ShoppingCart(); + story.given(new Printable<>(currentUser, UserPrinter::print), + new Printable<>(shoppingCart, ShoppingCartPrinter::print)); + story.when("Add oranges to the cart", () -> { + shoppingCart.add("Oranges", 1, BigDecimal.valueOf(5)); + return null; + }); + story.when("Add apples to the cart", () -> { + shoppingCart.add("Apples", 3, BigDecimal.valueOf(4)); + return null; + }); + Approvals.verify(story.then()); + } } -class User { - public String name = "Bob"; +class User +{ + public String name = "Bob"; } -class Item { - public String name; - public Integer quantity; - public BigDecimal price; - - public BigDecimal amount() { - return price.multiply(BigDecimal.valueOf(quantity)); - } - public Item(String itemName, int quantity, BigDecimal price) { - this.name = itemName; - this.quantity = quantity; - this.price = price; - } +class Item +{ + public String name; + public Integer quantity; + public BigDecimal price; + public BigDecimal amount() + { + return price.multiply(BigDecimal.valueOf(quantity)); + } + public Item(String itemName, int quantity, BigDecimal price) + { + this.name = itemName; + this.quantity = quantity; + this.price = price; + } } -class ShoppingCart { - public List articles = new ArrayList<>(); - public BigDecimal subtotal() { - return articles.stream().map(Item::amount).reduce(BigDecimal.ZERO, BigDecimal::add); - } - - public BigDecimal shipping() { - return BigDecimal.ONE; - } - public BigDecimal total() { - return this.subtotal().add(this.shipping()); - } - - public void add(String itemName, int quantity, BigDecimal price) { - this.articles.add(new Item(itemName, quantity, price)); - } +class ShoppingCart +{ + public List articles = new ArrayList<>(); + public BigDecimal subtotal() + { + return articles.stream().map(Item::amount).reduce(BigDecimal.ZERO, BigDecimal::add); + } + public BigDecimal shipping() + { + return BigDecimal.ONE; + } + public BigDecimal total() + { + return this.subtotal().add(this.shipping()); + } + public void add(String itemName, int quantity, BigDecimal price) + { + this.articles.add(new Item(itemName, quantity, price)); + } } -class ShoppingCartPrinter { - - public static String print(ShoppingCart shoppingCart) { - StringBuilder result = new StringBuilder(); - result.append("ShoppingCart:\n"); - result.append("Articles:\n"); - result.append(shoppingCart.articles.stream() - .map(article -> " " + article.name + " price: " + article.price + " quantity: " + article.quantity) - .collect(Collectors.joining("\n"))); - result.append("\n"); - - result.append("Subtotal:" + shoppingCart.subtotal() + "\n"); - result.append("Shipping:" + shoppingCart.shipping() + "\n"); - result.append("Total:" + shoppingCart.total() + "\n"); - return result.toString(); - } +class ShoppingCartPrinter +{ + public static String print(ShoppingCart shoppingCart) + { + StringBuilder result = new StringBuilder(); + result.append("ShoppingCart:\n"); + result.append("Articles:\n"); + result.append(shoppingCart.articles.stream() + .map(article -> " " + article.name + " price: " + article.price + " quantity: " + article.quantity) + .collect(Collectors.joining("\n"))); + result.append("\n"); + result.append("Subtotal:" + shoppingCart.subtotal() + "\n"); + result.append("Shipping:" + shoppingCart.shipping() + "\n"); + result.append("Total:" + shoppingCart.total() + "\n"); + return result.toString(); + } } -class UserPrinter { - public static String print(User user) { - return "User: " + user.name + "\n"; - } +class UserPrinter +{ + public static String print(User user) + { + return "User: " + user.name + "\n"; + } } \ No newline at end of file diff --git a/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java b/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java index 7e5beb1d6..e1faec157 100644 --- a/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java +++ b/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java @@ -5,67 +5,69 @@ import java.util.Collections; import java.util.concurrent.Callable; -public class PrintableScenario { - - - private final StringBuilder toVerify = new StringBuilder(); - private final String name; - private final String description; - private final ArrayList printables = new ArrayList<>(); - - public PrintableScenario(String name, String description) { - this.name = name; - this.description = description; - } - public PrintableScenario(String name) { - this(name, ""); - } - - public void given(Printable... printables) { - arrange(printables); - } - - public void arrange(Printable... printables) { - this.printables.addAll(Arrays.asList(printables)); - toVerify.append(makeHeading(name, description, "=")); - toVerify.append("\n"); - toVerify.append(printAll()); - } - - public String makeHeading(String heading, String summary, String underlineCharacter) { - int count = heading.length(); - // create a string made up of n copies of string "=" - String underlineHeading = String.join("", Collections.nCopies(count, underlineCharacter)); - return heading + "\n" + underlineHeading + "\n" + summary + "\n"; - } - - public String printAll() { - StringBuilder result = new StringBuilder(); - for (Printable printable : this.printables) { - result.append(printable.toString()); - result.append("\n"); - } - return result.toString(); - } - - public void when(String action, Callable function) throws Exception { - function.call(); - when(action); - } - - public void when(String action) { - act(action); - } - - public void act(String action) { - toVerify.append(makeHeading(action, "", "-")); - toVerify.append(printAll()); - } - public String then() { - return print(); - } - - public String print() { - return toVerify.toString(); +public class PrintableScenario +{ + private final StringBuilder toVerify = new StringBuilder(); + private final String name; + private final String description; + private final ArrayList printables = new ArrayList<>(); + public PrintableScenario(String name, String description) + { + this.name = name; + this.description = description; + } + public PrintableScenario(String name) + { + this(name, ""); + } + public void given(Printable... printables) + { + arrange(printables); + } + public void arrange(Printable... printables) + { + this.printables.addAll(Arrays.asList(printables)); + toVerify.append(makeHeading(name, description, "=")); + toVerify.append("\n"); + toVerify.append(printAll()); + } + public String makeHeading(String heading, String summary, String underlineCharacter) + { + int count = heading.length(); + // create a string made up of n copies of string "=" + String underlineHeading = String.join("", Collections.nCopies(count, underlineCharacter)); + return heading + "\n" + underlineHeading + "\n" + summary + "\n"; + } + public String printAll() + { + StringBuilder result = new StringBuilder(); + for (Printable printable : this.printables) + { + result.append(printable.toString()); + result.append("\n"); } + return result.toString(); + } + public void when(String action, Callable function) throws Exception + { + function.call(); + when(action); + } + public void when(String action) + { + act(action); + } + public void act(String action) + { + toVerify.append(makeHeading(action, "", "-")); + toVerify.append(printAll()); + } + public String then() + { + return print(); + } + public String print() + { + return toVerify.toString(); + } } From 2e7293d5bab0d116fff4e320d716fa8af6db0d50 Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 11 Jan 2024 13:09:56 +0100 Subject: [PATCH 3/9] Actually have the User class do something --- .../approvaltests/bdd/PrintableScenarioTest.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java index eaa483c63..9643370ef 100644 --- a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java @@ -8,6 +8,7 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; public class PrintableScenarioTest @@ -17,7 +18,7 @@ public void printBDDScenario() throws Exception { PrintableScenario story = new PrintableScenario("Buy Groceries", "Bob puts two items in the shopping cart"); User currentUser = new User(); - ShoppingCart shoppingCart = new ShoppingCart(); + ShoppingCart shoppingCart = new ShoppingCart(currentUser); story.given(new Printable<>(currentUser, UserPrinter::print), new Printable<>(shoppingCart, ShoppingCartPrinter::print)); story.when("Add oranges to the cart", () -> { @@ -56,14 +57,21 @@ public Item(String itemName, int quantity, BigDecimal price) class ShoppingCart { - public List articles = new ArrayList<>(); + private final User user; + public List articles = new ArrayList<>(); + public ShoppingCart(User currentUser) + { + this.user = currentUser; + } public BigDecimal subtotal() { return articles.stream().map(Item::amount).reduce(BigDecimal.ZERO, BigDecimal::add); } public BigDecimal shipping() { - return BigDecimal.ONE; + if (Objects.equals(user.name, "Bob")) + { return BigDecimal.ONE; } + return BigDecimal.ZERO; } public BigDecimal total() { From bce16385d115033c285e8ef3eb43a69de830679e Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 11 Jan 2024 13:07:09 +0100 Subject: [PATCH 4/9] New checked exception in the PrintableScenario class. Not sure if this is a problem. --- .../CheckedExceptionsTest.testTheVerifyApi.approved.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/approvaltests-tests/src/test/java/org/approvaltests/CheckedExceptionsTest.testTheVerifyApi.approved.txt b/approvaltests-tests/src/test/java/org/approvaltests/CheckedExceptionsTest.testTheVerifyApi.approved.txt index 5769cf0e9..268f7d6c9 100644 --- a/approvaltests-tests/src/test/java/org/approvaltests/CheckedExceptionsTest.testTheVerifyApi.approved.txt +++ b/approvaltests-tests/src/test/java/org/approvaltests/CheckedExceptionsTest.testTheVerifyApi.approved.txt @@ -1,6 +1,7 @@ Methods with checked exceptions +org.approvaltests.strings.PrintableScenario.when org.lambda.actions.Action0WithException.call org.lambda.actions.Action1WithException.call org.lambda.actions.Action2WithException.call From a308a2ce36ba1e5ad6f97ba6427cf967823a3cf6 Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 11 Jan 2024 16:49:54 +0100 Subject: [PATCH 5/9] Improve docs with snippet --- .../java/org/approvaltests/bdd/PrintableScenarioTest.java | 2 ++ approvaltests/docs/how_to/BddScenarios.md | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java index 9643370ef..d2cf9bf0c 100644 --- a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java @@ -13,6 +13,7 @@ public class PrintableScenarioTest { + // begin-snippet: bdd_test @Test public void printBDDScenario() throws Exception { @@ -31,6 +32,7 @@ public void printBDDScenario() throws Exception }); Approvals.verify(story.then()); } + // end-snippet } class User diff --git a/approvaltests/docs/how_to/BddScenarios.md b/approvaltests/docs/how_to/BddScenarios.md index 2236c1fe1..47f803841 100644 --- a/approvaltests/docs/how_to/BddScenarios.md +++ b/approvaltests/docs/how_to/BddScenarios.md @@ -14,14 +14,18 @@ It doesn't use Gherkin strictly, but you still get a scenario with a name, descr ## Sample Code -I need some help with this part of the documentation. I'd like to include PrintableScenarioTest here as an example. +This test case uses a PrintableScenario: + -* Construct your scenario at the beginning of the test with a descriptive name and summary. +* Construct the scenario at the beginning of the test with a descriptive name and summary. * When you are done with the code for the "given" step of the test, call story.given() with your list of Printables. Use one printable to wrap each object you have created which you think might change state during the test and that you want to verify. * In each "When" step of your test, call when() with a descriptive name for what the user is doing. The second argument is a lambda that contains code to actually do the when step. This is optional, you can write it in your test as normal code before the call to 'when' if you prefer. * The "Then" step is a call to then() which will verify the state of all the Printables as it changed throughout the course of the test. +This produces a feature file like this one: [[PrintableScenarioTest.printBDDScenario.approved.txt](../../../Fapprovaltests-tests/src/test/javavorg/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt)] + Note - if you don't want to use the BDD terminology you don't have to. You can equally well use 'arrange', 'act', 'print' instead of 'given' 'when' 'then' + From 5ff570ef2376dc3b5b92d22d98de0196d073c251 Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 1 Feb 2024 09:57:53 +0100 Subject: [PATCH 6/9] Better support if an exception is thrown by the system under test --- ...rioTest.bddScenarioThatThrows.approved.txt | 26 ++++ .../bdd/PrintableScenarioTest.java | 24 +-- ...ScenarioTest.printBDDScenario.approved.txt | 2 - .../strings/PrintableScenario.java | 146 ++++++++++-------- 4 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.bddScenarioThatThrows.approved.txt diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.bddScenarioThatThrows.approved.txt b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.bddScenarioThatThrows.approved.txt new file mode 100644 index 000000000..c58e27fca --- /dev/null +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.bddScenarioThatThrows.approved.txt @@ -0,0 +1,26 @@ +Shoplifting +=========== +We expect an exception + +User: Bob + +ShoppingCart: +Articles: + +Subtotal:0 +Shipping:1 +Total:1 + +Something illegal +----------------- +This action threw an exception: java.lang.RuntimeException: shoplifting + +User: Bob + +ShoppingCart: +Articles: + +Subtotal:0 +Shipping:1 +Total:1 + diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java index d2cf9bf0c..74107b46f 100644 --- a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java @@ -22,17 +22,23 @@ public void printBDDScenario() throws Exception ShoppingCart shoppingCart = new ShoppingCart(currentUser); story.given(new Printable<>(currentUser, UserPrinter::print), new Printable<>(shoppingCart, ShoppingCartPrinter::print)); - story.when("Add oranges to the cart", () -> { - shoppingCart.add("Oranges", 1, BigDecimal.valueOf(5)); - return null; - }); - story.when("Add apples to the cart", () -> { - shoppingCart.add("Apples", 3, BigDecimal.valueOf(4)); - return null; - }); - Approvals.verify(story.then()); + story.when("Add oranges to the cart", () -> shoppingCart.add("Oranges", 1, BigDecimal.valueOf(5))); + story.when("Add apples to the cart", () -> shoppingCart.add("Apples", 3, BigDecimal.valueOf(4))); + Approvals.verify(story); } // end-snippet + + @Test + public void bddScenarioThatThrows() throws Exception + { + PrintableScenario story = new PrintableScenario("Shoplifting", "We expect an exception"); + User currentUser = new User(); + ShoppingCart shoppingCart = new ShoppingCart(currentUser); + story.given(new Printable<>(currentUser, UserPrinter::print), + new Printable<>(shoppingCart, ShoppingCartPrinter::print)); + story.when("Something illegal", () -> {throw new RuntimeException("shoplifting");}); + Approvals.verify(story); + } } class User diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt index db5a989c7..16406054b 100644 --- a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt @@ -13,7 +13,6 @@ Total:1 Add oranges to the cart ----------------------- - User: Bob ShoppingCart: @@ -25,7 +24,6 @@ Total:6 Add apples to the cart ---------------------- - User: Bob ShoppingCart: diff --git a/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java b/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java index e1faec157..4b165209a 100644 --- a/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java +++ b/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java @@ -1,73 +1,87 @@ package org.approvaltests.strings; +import org.lambda.actions.Action0WithException; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.concurrent.Callable; - -public class PrintableScenario -{ - private final StringBuilder toVerify = new StringBuilder(); - private final String name; - private final String description; - private final ArrayList printables = new ArrayList<>(); - public PrintableScenario(String name, String description) - { - this.name = name; - this.description = description; - } - public PrintableScenario(String name) - { - this(name, ""); - } - public void given(Printable... printables) - { - arrange(printables); - } - public void arrange(Printable... printables) - { - this.printables.addAll(Arrays.asList(printables)); - toVerify.append(makeHeading(name, description, "=")); - toVerify.append("\n"); - toVerify.append(printAll()); - } - public String makeHeading(String heading, String summary, String underlineCharacter) - { - int count = heading.length(); - // create a string made up of n copies of string "=" - String underlineHeading = String.join("", Collections.nCopies(count, underlineCharacter)); - return heading + "\n" + underlineHeading + "\n" + summary + "\n"; - } - public String printAll() - { - StringBuilder result = new StringBuilder(); - for (Printable printable : this.printables) - { - result.append(printable.toString()); - result.append("\n"); + +public class PrintableScenario { + private final StringBuilder toVerify = new StringBuilder(); + private final String name; + private final String description; + private final ArrayList printables = new ArrayList<>(); + + public PrintableScenario(String name, String description) { + this.name = name; + this.description = description; + } + + public PrintableScenario(String name) { + this(name, ""); + } + + public void given(Printable... printables) { + arrange(printables); + } + + public void arrange(Printable... printables) { + this.printables.addAll(Arrays.asList(printables)); + toVerify.append(makeHeading(name, description, "=")); + toVerify.append(printAll()); + } + + public String makeHeading(String heading, String summary, String underlineCharacter) { + int count = heading.length(); + // create a string made up of n copies of underlineCharacter + String underlineHeading = String.join("", Collections.nCopies(count, underlineCharacter)); + + String result = heading + "\n" + underlineHeading + "\n"; + if (summary != null && !summary.isEmpty()) { + result += summary + "\n\n"; + } + return result; + } + + public String printAll() { + StringBuilder result = new StringBuilder(); + for (Printable printable : this.printables) { + result.append(printable.toString()); + result.append("\n"); + } + return result.toString(); + } + + public void when(String action, Action0WithException function) { + try { + function.call(); + when(action); + } catch (Throwable e) { + toVerify.append(makeHeading(action, + "This action threw an exception: " + e, "-")); + toVerify.append(printAll()); + } + } + + public void when(String action) { + act(action); + } + + public void act(String action) { + toVerify.append(makeHeading(action, "", "-")); + toVerify.append(printAll()); + } + + public String then() { + return print(); + } + + public String print() { + return toVerify.toString(); + } + + @Override + public String toString() { + return print(); } - return result.toString(); - } - public void when(String action, Callable function) throws Exception - { - function.call(); - when(action); - } - public void when(String action) - { - act(action); - } - public void act(String action) - { - toVerify.append(makeHeading(action, "", "-")); - toVerify.append(printAll()); - } - public String then() - { - return print(); - } - public String print() - { - return toVerify.toString(); - } } From 86286b8135d6365aba2f73d52d77c57730bff6da Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 1 Feb 2024 09:58:06 +0100 Subject: [PATCH 7/9] tweak docs --- approvaltests/docs/how_to/BddScenarios.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/approvaltests/docs/how_to/BddScenarios.md b/approvaltests/docs/how_to/BddScenarios.md index 47f803841..d072d8e13 100644 --- a/approvaltests/docs/how_to/BddScenarios.md +++ b/approvaltests/docs/how_to/BddScenarios.md @@ -22,10 +22,10 @@ This test case uses a PrintableScenario: * Construct the scenario at the beginning of the test with a descriptive name and summary. * When you are done with the code for the "given" step of the test, call story.given() with your list of Printables. Use one printable to wrap each object you have created which you think might change state during the test and that you want to verify. -* In each "When" step of your test, call when() with a descriptive name for what the user is doing. The second argument is a lambda that contains code to actually do the when step. This is optional, you can write it in your test as normal code before the call to 'when' if you prefer. -* The "Then" step is a call to then() which will verify the state of all the Printables as it changed throughout the course of the test. +* In each "When" step of your test, call when() with a descriptive name for what the user is doing. The second argument is a lambda that contains code to actually do the 'when' step. This is optional, you can write it in your test as normal code before the call to 'when' if you prefer. +* The "Then" step is a call to then() which you pass to Approvals.Verify. This will verify the state of all the Printables as they changed throughout the course of the test. -This produces a feature file like this one: [[PrintableScenarioTest.printBDDScenario.approved.txt](../../../Fapprovaltests-tests/src/test/javavorg/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt)] +This produces a feature file like this one: [[PrintableScenarioTest.printBDDScenario.approved.txt](../../../approvaltests-tests/src/test/javavorg/approvaltests/bdd/PrintableScenarioTest.printBDDScenario.approved.txt)] Note - if you don't want to use the BDD terminology you don't have to. You can equally well use 'arrange', 'act', 'print' instead of 'given' 'when' 'then' From 29c0d9dbb442b51f6efd7905e6d044830f586f26 Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 1 Feb 2024 09:59:57 +0100 Subject: [PATCH 8/9] PrintableScenario no longer throws a checked exception --- .../CheckedExceptionsTest.testTheVerifyApi.approved.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/approvaltests-tests/src/test/java/org/approvaltests/CheckedExceptionsTest.testTheVerifyApi.approved.txt b/approvaltests-tests/src/test/java/org/approvaltests/CheckedExceptionsTest.testTheVerifyApi.approved.txt index 268f7d6c9..5769cf0e9 100644 --- a/approvaltests-tests/src/test/java/org/approvaltests/CheckedExceptionsTest.testTheVerifyApi.approved.txt +++ b/approvaltests-tests/src/test/java/org/approvaltests/CheckedExceptionsTest.testTheVerifyApi.approved.txt @@ -1,7 +1,6 @@ Methods with checked exceptions -org.approvaltests.strings.PrintableScenario.when org.lambda.actions.Action0WithException.call org.lambda.actions.Action1WithException.call org.lambda.actions.Action2WithException.call From d85214bbca5ada98bc721831beb1ab61bfe43ee0 Mon Sep 17 00:00:00 2001 From: emily Date: Thu, 1 Feb 2024 10:00:03 +0100 Subject: [PATCH 9/9] fix formatting --- .../bdd/PrintableScenarioTest.java | 9 +- .../strings/PrintableScenario.java | 152 +++++++++--------- 2 files changed, 83 insertions(+), 78 deletions(-) diff --git a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java index 74107b46f..1d59b7c19 100644 --- a/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java +++ b/approvaltests-tests/src/test/java/org/approvaltests/bdd/PrintableScenarioTest.java @@ -23,11 +23,10 @@ public void printBDDScenario() throws Exception story.given(new Printable<>(currentUser, UserPrinter::print), new Printable<>(shoppingCart, ShoppingCartPrinter::print)); story.when("Add oranges to the cart", () -> shoppingCart.add("Oranges", 1, BigDecimal.valueOf(5))); - story.when("Add apples to the cart", () -> shoppingCart.add("Apples", 3, BigDecimal.valueOf(4))); + story.when("Add apples to the cart", () -> shoppingCart.add("Apples", 3, BigDecimal.valueOf(4))); Approvals.verify(story); } // end-snippet - @Test public void bddScenarioThatThrows() throws Exception { @@ -35,8 +34,10 @@ public void bddScenarioThatThrows() throws Exception User currentUser = new User(); ShoppingCart shoppingCart = new ShoppingCart(currentUser); story.given(new Printable<>(currentUser, UserPrinter::print), - new Printable<>(shoppingCart, ShoppingCartPrinter::print)); - story.when("Something illegal", () -> {throw new RuntimeException("shoplifting");}); + new Printable<>(shoppingCart, ShoppingCartPrinter::print)); + story.when("Something illegal", () -> { + throw new RuntimeException("shoplifting"); + }); Approvals.verify(story); } } diff --git a/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java b/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java index 4b165209a..080e9031d 100644 --- a/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java +++ b/approvaltests/src/main/java/org/approvaltests/strings/PrintableScenario.java @@ -6,82 +6,86 @@ import java.util.Arrays; import java.util.Collections; -public class PrintableScenario { - private final StringBuilder toVerify = new StringBuilder(); - private final String name; - private final String description; - private final ArrayList printables = new ArrayList<>(); - - public PrintableScenario(String name, String description) { - this.name = name; - this.description = description; - } - - public PrintableScenario(String name) { - this(name, ""); +public class PrintableScenario +{ + private final StringBuilder toVerify = new StringBuilder(); + private final String name; + private final String description; + private final ArrayList printables = new ArrayList<>(); + public PrintableScenario(String name, String description) + { + this.name = name; + this.description = description; + } + public PrintableScenario(String name) + { + this(name, ""); + } + public void given(Printable... printables) + { + arrange(printables); + } + public void arrange(Printable... printables) + { + this.printables.addAll(Arrays.asList(printables)); + toVerify.append(makeHeading(name, description, "=")); + toVerify.append(printAll()); + } + public String makeHeading(String heading, String summary, String underlineCharacter) + { + int count = heading.length(); + // create a string made up of n copies of underlineCharacter + String underlineHeading = String.join("", Collections.nCopies(count, underlineCharacter)); + String result = heading + "\n" + underlineHeading + "\n"; + if (summary != null && !summary.isEmpty()) + { + result += summary + "\n\n"; } - - public void given(Printable... printables) { - arrange(printables); + return result; + } + public String printAll() + { + StringBuilder result = new StringBuilder(); + for (Printable printable : this.printables) + { + result.append(printable.toString()); + result.append("\n"); } - - public void arrange(Printable... printables) { - this.printables.addAll(Arrays.asList(printables)); - toVerify.append(makeHeading(name, description, "=")); - toVerify.append(printAll()); + return result.toString(); + } + public void when(String action, Action0WithException function) + { + try + { + function.call(); + when(action); } - - public String makeHeading(String heading, String summary, String underlineCharacter) { - int count = heading.length(); - // create a string made up of n copies of underlineCharacter - String underlineHeading = String.join("", Collections.nCopies(count, underlineCharacter)); - - String result = heading + "\n" + underlineHeading + "\n"; - if (summary != null && !summary.isEmpty()) { - result += summary + "\n\n"; - } - return result; - } - - public String printAll() { - StringBuilder result = new StringBuilder(); - for (Printable printable : this.printables) { - result.append(printable.toString()); - result.append("\n"); - } - return result.toString(); - } - - public void when(String action, Action0WithException function) { - try { - function.call(); - when(action); - } catch (Throwable e) { - toVerify.append(makeHeading(action, - "This action threw an exception: " + e, "-")); - toVerify.append(printAll()); - } - } - - public void when(String action) { - act(action); - } - - public void act(String action) { - toVerify.append(makeHeading(action, "", "-")); - toVerify.append(printAll()); - } - - public String then() { - return print(); - } - - public String print() { - return toVerify.toString(); - } - - @Override - public String toString() { - return print(); + catch (Throwable e) + { + toVerify.append(makeHeading(action, "This action threw an exception: " + e, "-")); + toVerify.append(printAll()); } + } + public void when(String action) + { + act(action); + } + public void act(String action) + { + toVerify.append(makeHeading(action, "", "-")); + toVerify.append(printAll()); + } + public String then() + { + return print(); + } + public String print() + { + return toVerify.toString(); + } + @Override + public String toString() + { + return print(); + } }