Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

F!! Printable scenario - to better support BDD #460

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
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.Objects;
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(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());
}
}

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
{
private final User user;
public List<Item> 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()
{
if (Objects.equals(user.name, "Bob"))
{ return BigDecimal.ONE; }
return BigDecimal.ZERO;
}
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";
}
}
Original file line number Diff line number Diff line change
@@ -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

27 changes: 27 additions & 0 deletions approvaltests/docs/how_to/BddScenarios.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<a id="top"></a>

# How to make a BDD Scenario using Approvals
<!-- toc -->
## Contents

* [Introduction](#introduction)
* [Sample Code](#sample-code)<!-- endToc -->

## 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.
<!-- snippet: bdd_test -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what you need is the following. These comments in the java file:

// begin-snippet: MySnippetName
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(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());
  }
}
// end-snippet

and then you can reference it here in markdown with snippet: MySnippetName


<!-- endSnippet -->

* 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'
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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<Printable> 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();
}
}
Loading