Unveiling the Power of Approval Testing

Unveiling the Power of Approval Testing


Janina Nemec
I am…
👩💻 a Software Developer/Crafter
a co-organizer for a security conference
I like…
💡Learning & Improving ⇒ Software Teaming
🔄 Automatization
🎨 Drawing
👩🌾 Gardening
🎮 Video Games
🐶 Dogs 💜


class ShopOrderTest {
@Test
void assertionTest() throws JsonProcessingException {
// given
String orderId = "someOrderId";
ShopOrder order = aDefaultOrder(orderId);
// when
anOrderWasProcessed(order);
// then
OrderResult orderResult = jsonMapper.readValue(callRestEndpoint(orderId), OrderResult.class);
assertThat(orderResult.getId()).isEqualTo("someOrderId");
assertThat(orderResult.getVersion()).isEqualTo(1);
ItemResult item = orderResult.getItems().getFirst();
assertThat(item.getId()).isEqualTo("someItemId");
assertThat(item.getName()).isEqualTo("ATD 3 Conf. Days");
assertThat(item.getAmount()).isEqualTo(2);
PriceResult itemPrice = item.getPrice();
assertThat(itemPrice.getValue()).isEqualTo(225000);
assertThat(itemPrice.getMonetaryUnit()).isEqualTo("cent");
assertThat(itemPrice.getCurrency()).isEqualTo("EUR");
CouponResult coupon = orderResult.getCoupons().getFirst();
assertThat(coupon.getId()).isEqualTo("someCouponId");
assertThat(coupon.getDescription()).isEqualTo("Speaker Coupon");
assertThat(coupon.getReducedRateInPercentage()).isEqualTo(100);
assertThat(orderResult.getOrderTimeStamp()).isEqualTo(LocalDateTime.of(2024, 7, 19, 11, 45));
assertThat(orderResult.getDeliveryDate()).isEqualTo(LocalDate.of(2024, 11, 22));
PriceResult shippingCost = orderResult.getShippingCost().getFirst();
assertThat(shippingCost.getValue()).isEqualTo(500);
assertThat(shippingCost.getMonetaryUnit()).isEqualTo("cent");
assertThat(shippingCost.getCurrency()).isEqualTo("EUR");
CustomerResult customer = orderResult.getCustomer();
assertThat(customer.getId()).isEqualTo("someCustomerId");
assertThat(customer.getFirstName()).isEqualTo("REWE");
assertThat(customer.getLastName()).isEqualTo("Digital");
AddressResult shippingAddress = orderResult.getShippingAddress();
assertThat(shippingAddress.getId()).isEqualTo("someShippingAddressId");
assertThat(shippingAddress.getFirstName()).isEqualTo("Janina");
assertThat(shippingAddress.getLastName()).isEqualTo("Nemec");
assertThat(shippingAddress.getStreetName()).isEqualTo("Schanzenstr.");
assertThat(shippingAddress.getHouseNumber()).isEqualTo("6-20");
assertThat(shippingAddress.getPostalCode()).isEqualTo("51063");
assertThat(shippingAddress.getCity()).isEqualTo("Köln");
assertThat(shippingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(shippingAddress.getPhone()).isEqualTo("0221 9758420");
assertThat(shippingAddress.getLatitude()).isEqualTo("50.96490882194811");
assertThat(shippingAddress.getLongitude()).isEqualTo("7.014472855463499");
assertThat(shippingAddress.getEmail()).isEqualTo("kontakt@rewe-digital.com");
AddressResult billingAddress = orderResult.getBillingAddress();
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Micha");
assertThat(billingAddress.getLastName()).isEqualTo("Kutz");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}
class ShopOrderTest {
@Test
void assertionTest() throws JsonProcessingException {
// given
String orderId = "someOrderId";
ShopOrder order = aDefaultOrder(orderId);
// when
anOrderWasProcessed(order);
// then
JsonApprovals.verifyJson(callRestEndpoint(orderId));
}
}


record Address(
String id,
String firstName,
String lastName,
String streetName,
String houseNumber,
String city,
String country,
String phone,
String latitude,
String longitude,
String email,
String postalCode
) {}
record Order(
String id,
int version,
List<Item> items,
List<Coupon> coupons,
LocalDateTime orderTimeStamp,
LocalDate deliveryDate,
List<Price> shippingCost,
Customer customer,
Address shippingAddress,
Address billingAddress
) {}
record Item(
String id,
String name,
int amount,
Price price
) {}
record Coupon(
String id,
String description,
int reducedRateInPercentage
) {}
record Price(
int value,
String monetaryUnit,
String currency
) {}
record Customer(
String id,
String firstName,
String lastName
) {}

record Address(
String id,
String firstName,
String lastName,
String streetName,
String houseNumber,
String city,
String country,
String phone,
String latitude,
String longitude,
String email,
String postalCode
) {}
Green Assertion Tests

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstr.").houseNumber("20")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}

v ✅ Test Results
v ✅ AddressAssertionsTest
✅ assertionTest

v ✅ Test Results
v ✅ AddressAssertionsTest
✅ assertionTest
Problem
- Are the tests good?
- Do they test all fields?
- Do they test the correct thing?

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstr.").houseNumber("20")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}
class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstr.").houseNumber("20")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}

A Red Assertion

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstra.").houseNumber("21")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}

v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Expected :"Domstr."
Actual :"Domstra."
at AddressAssertionTest.java:19

Problem
- Only first failing assertion is visible
- Are there multiple errors?
- Is there an invisible pattern?
v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Expected :"Domstr."
Actual :"Domstra."
at AddressAssertionTest.java:19

Soft Assertions

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(...)
.build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(...)
.build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
SoftAssertions.assertSoftly( softly -> {
softly.assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
softly.assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
softly.assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
softly.assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
softly.assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
softly.assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
softly.assertThat(billingAddress.getCity()).isEqualTo("Köln");
softly.assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
softly.assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
softly.assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
softly.assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
softly.assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
softly.assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
});
}
}

v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Multiple Failures (2 failures)
-- failure 1 --
expected :"Domstr."
but was :"Domstra."
at AddressAssertionTest.java:16
-- failure 2 --
expected :"20"
but was :"21"
at AddressAssertionTest.java:17

Improvements
- All failing assertions visible
- Easy to read
v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Multiple Failures (2 failures)
-- failure 1 --
expected :"Domstr."
but was :"Domstra."
at AddressAssertionTest.java:16
-- failure 2 --
expected :"20"
but was :"21"
at AddressAssertionTest.java:17

Problem
- Missing Assertions
- A lot boilerplate code
- Not used much
- Still not testing the right thing
v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Multiple Failures (2 failures)
-- failure 1 --
expected :"Domstr."
but was :"Domstra."
at AddressAssertionTest.java:16
-- failure 2 --
expected :"20"
but was :"21"
at AddressAssertionTest.java:17

Object Assertion

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstra.").houseNumber("21")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstra.").houseNumber("21")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
AddressResult expectedBillingAddress = anAddressResult().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstr.").houseNumber("20")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com")
.latitude("50.94603935915518").longitude("6.959302840118697")
.status(CustomerStatus.NEW_CUSTOMER)
.build();
assertThat(billingAddress).isEqualTo(expectedBillingAddress);
}
}

v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Expected :AddressResult(id=someBillingAddressId, firstName=Janina, lastName=Nemec, streetName=Domstr., houseNumber=20, city=Köln, country=Deutschland, phone=+49 221 1490, latitude=50.94603935915518, longitude=6.959302840118697, email=info@rewe-group.com, posta ...
Actual :AddressResult(id=someBillingAddressId, firstName=Janina, lastName=Nemec, streetName=Domstra., houseNumber=21, city=Köln, country=Deutschland, phone=+49 221 1490, latitude=50.94603935915518, longitude=6.959302840118697, email=info@rewe-group.com, post ...
<Click to see difference>

v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Expected :AddressResult(id=someBillingAddressId, firstName=Janina, lastName=Nemec, streetName=Domstr., houseNumber=20, city=Köln, country=Deutschland, phone=+49 221 1490, latitude=50.94603935915518, longitude=6.959302840118697, email=info@rewe-group.com, posta ...
Actual :AddressResult(id=someBillingAddressId, firstName=Janina, lastName=Nemec, streetName=Domstra., houseNumber=21, city=Köln, country=Deutschland, phone=+49 221 1490, latitude=50.94603935915518, longitude=6.959302840118697, email=info@rewe-group.com, post ...
<Click to see difference>
Problem
- Failure message hard to read
- Are there multiple errors?
- Diff not assessable



Problem
- Diff is unpleasent to read
- Differences can be overlooked
- Not always feasible
- Still not testing the right thing


Improvements
- All failures are visible
- All data is tested


One Text Assertion

class AddressAssertionsTest {
@Test
void assertionTest() {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(...)
.build();
anOrderWasProcessed(shopOrder);
assertThat(callRestEndpointForBillingAddress(orderId)).isEqualToIgnoringWhitespace("""
{
"id": "someBillingAddressId",
"firstName": "Janina",
"lastName": "Nemec",
"streetName": "Domstr.",
"houseNumber": "20",
"city": "Köln",
"country": "Deutschland",
"phone": "+49 221 1490",
"latitude": "50.94603935915518",
"longitude": "6.959302840118697",
"email": "info@rewe-group.com",
"postalCode": "50668",
"status":"new_customer"
}""");
}
}

class AddressAssertionsTest {
private String TEST_DIR = "src/test/resources/json/FileComparisonTest/";
@Test
void assertionTest() {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(...)
.build();
anOrderWasProcessed(shopOrder);
assertThat(callRestEndpointForBillingAddress(orderId)).isEqualToIgnoringWhitespace(
new String(Files.readAllBytes(Paths.get(TEST_DIR + "expectedBillingAddress.json")))
);
}
}

v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Expecting actual:
"{"id":"someBillingAddressId", "firstName":"Janina", "lastName":"Nemec", "streetName":"Domstr.", "houseNumber":"20", "city":"Köln", "country":"Deutschland", "phone":"+49 221 1490", "status":"new_customer", "latitude":"50.94603935915518", "longitude":"6.959302840118697", "email":"info@rewe-group.com", "postalCode":"50668"}"
to be equal to:
"{
"id": "someBillingAddressId",
"firstName": "Janina",
"lastName": "Nemec",
"streetName": "Domstr.",
"houseNumber": "20",
"city": "Köln",
"country": "Deutschland",
"phone": "+49 221 1490",
"latitude": "50.94603935915518",
"longitude": "6.959302840118697",
"email": "info@rewe-group.com",
"postalCode": "50668"
}
"
when ignoring whitespace differences
<Click to see difference>

v ❌ Test Results
v ❌ AddressAssertionsTest
❌ assertionTest
Expecting actual:
"{"id":"someBillingAddressId", "firstName":"Janina", "lastName":"Nemec", "streetName":"Domstr.", "houseNumber":"20", "city":"Köln", "country":"Deutschland", "phone":"+49 221 1490", "status":"new_customer", "latitude":"50.94603935915518", "longitude":"6.959302840118697", "email":"info@rewe-group.com", "postalCode":"50668"}"
to be equal to:
"{
"id": "someBillingAddressId",
"firstName": "Janina",
"lastName": "Nemec",
"streetName": "Domstr.",
"houseNumber": "20",
"city": "Köln",
"country": "Deutschland",
"phone": "+49 221 1490",
"latitude": "50.94603935915518",
"longitude": "6.959302840118697",
"email": "info@rewe-group.com",
"postalCode": "50668"
}
"
when ignoring whitespace differences
<Click to see difference>
Problem
- Failure messages not readable
- Hard to know what is going on
- These tests are hard to maintain



Problem
- Diff is unpleasent to read
- Differences can be overlooked
- False positives can be highlighted


Improvements
- All failures are visible
- All data is tested
- We test the right thing


THE INCIDENT

THE INCIDENT

THE INCIDENT

> ✅ All Tests
✔️ Code Review


Merged
THE INCIDENT


{
"id": "someBillingAddressId",
"streetName": "Domstr.",
"houseNumber": "20",
"firstName": "Micha",
"lastName": "Kutz",
"city": "Köln",
"country": "Deutschland",
"phone": "+49 221 1490",
"status": "new_customer",
"latitude": "50.94603935915518",
"longitude": "6.959302840118697",
"postalCode": "50668",
"email": "info@rewe-group.com"
}{
"id": "anotherBillingAddressId",
"firstName": "Janina",
"lastName": "Nemec",
"streetName": "Schanzenstr.",
"houseNumber": "6-20",
"city": "Köln-Mülheim",
"country": "Deutschland",
"phone": "0221 9758420",
"latitude": "50.96490882194811",
"longitude": "7.014472855463499",
"status": "KNOWN_CUSTOMER",
"email": "kontakt@rewe-digital.com",
"postalCode": "51063"
}
Before
After
public enum OldStatus {
NEW_CUSTOMER,
KNOWN_CUSTOMER;
@JsonValue
String value() { return this.name().toLowerCase(); }
}
public enum NewStatus {
NEW_CUSTOMER,
KNOWN_CUSTOMER;
}
> ✅ All Tests

All the tests were green
But how?

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstr.").houseNumber("20")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstr.").houseNumber("20")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}
public enum CustomerStatus {
NEW_CUSTOMER,
KNOWN_CUSTOMER;
@JsonValue
String value() { return this.name().toLowerCase(); }
}
public enum NewStatus {
NEW_CUSTOMER,
KNOWN_CUSTOMER;
}
public enum NewStatus {
NEW_CUSTOMER,
KNOWN_CUSTOMER;
@JsonValue
String value() { return this.name().toLowerCase(); }
}
{
"id": "someBillingAddressId",
"streetName": "Domstr.",
"houseNumber": "20",
"firstName": "Micha",
"lastName": "Kutz",
"city": "Köln",
"country": "Deutschland",
"phone": "+49 221 1490",
"status": "new_customer",
"latitude": "50.94603935915518",
"longitude": "6.959302840118697",
"postalCode": "50668",
"email": "info@rewe-group.com"
}{
"id": "anotherBillingAddressId",
"firstName": "Janina",
"lastName": "Nemec",
"streetName": "Schanzenstr.",
"houseNumber": "6-20",
"city": "Köln-Mülheim",
"country": "Deutschland",
"phone": "0221 9758420",
"latitude": "50.96490882194811",
"longitude": "7.014472855463499",
"status": "KNOWN_CUSTOMER",
"email": "kontakt@rewe-digital.com",
"postalCode": "51063"
}
{
"id": "someBillingAddressId",
"streetName": "Domstr.",
"houseNumber": "20",
"firstName": "Micha",
"lastName": "Kutz",
"city": "Köln",
"country": "Deutschland",
"phone": "+49 221 1490",
"status": "new_customer",
"latitude": "50.94603935915518",
"longitude": "6.959302840118697",
"postalCode": "50668",
"email": "info@rewe-group.com"
}{
"id": "anotherBillingAddressId",
"firstName": "Janina",
"lastName": "Nemec",
"streetName": "Schanzenstr.",
"houseNumber": "6-20",
"city": "Köln-Mülheim",
"country": "Deutschland",
"phone": "0221 9758420",
"latitude": "50.96490882194811",
"longitude": "7.014472855463499",
"status": "known_customer",
"email": "kontakt@rewe-digital.com",
"postalCode": "51063"
}
How to prevent this in the future?

-
More file comparison Tests?
A lot of work to Maintain
Hard to read when they fail
-
Pact Tests?
A lot of setup necessary
Other teams are not interested
Cost to Benefit?
-
Approval Testing?
?
class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstr.").houseNumber("20")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
AddressResult billingAddress = jsonMapper.readValue(callRestEndpointForBillingAddress(orderId));
assertThat(billingAddress.getId()).isEqualTo("someBillingAddressId");
assertThat(billingAddress.getFirstName()).isEqualTo("Janina");
assertThat(billingAddress.getLastName()).isEqualTo("Nemec");
assertThat(billingAddress.getStreetName()).isEqualTo("Domstr.");
assertThat(billingAddress.getHouseNumber()).isEqualTo("20");
assertThat(billingAddress.getPostalCode()).isEqualTo("50668");
assertThat(billingAddress.getCity()).isEqualTo("Köln");
assertThat(billingAddress.getCountry()).isEqualTo("Deutschland");
assertThat(billingAddress.getPhone()).isEqualTo("+49 221 1490");
assertThat(billingAddress.getLatitude()).isEqualTo("50.94603935915518");
assertThat(billingAddress.getLongitude()).isEqualTo("6.959302840118697");
assertThat(billingAddress.getStatus()).isEqualTo(CustomerStatus.NEW_CUSTOMER);
assertThat(billingAddress.getEmail()).isEqualTo("info@rewe-group.com");
}
}

class AddressAssertionTest {
@Test
void assertionTest() throws JsonProcessingException {
String orderId = "someOrderId";
ShopOrder shopOrder = anyOrder(orderId)
.billingAddress(anAddress().id("someBillingAddressId")
.firstName("Janina").lastName("Nemec")
.streetName("Domstr.").houseNumber("20")
.postalCode("50668").city("Köln").country("Deutschland")
.phone("+49 221 1490").email("info@rewe-group.com").build()
).build();
anOrderWasProcessed(shopOrder);
JsonApprovals.verifyJson(callRestEndpointForBillingAddress(orderId));
}
}

Green Approval Tests

v ✅ Test Results
v ✅ AddressAssertionsTest
✅ assertionTest

v ✅ Test Results
v ✅ AddressAssertionsTest
✅ assertionTest

Benefits
- Easy to write
- Easy to maintain
- Less test code
- Everything is tested
- The right level is tested
- No handling of whitespaces
Red Approval Tests





Benefits
- Opens a diff tool already installed
- All deviations are visible
- Good overview
- No issues with whitespaces
- Test is easy to fix - if change is wanted


Problems?
- Remember to commit approved.json
- Test easy is to "fix" - if change is unintentional
- Is easily overused
Approval Tests

Usefull for:
-
Testing Interfaces
-
Legacy Code Refactoring
Be carefull of:
-
Approving to fast
-
Over-use
Outlook:
-
Visual Approval Tests
Live Coding

Workshop

https://github.com/IsItArtOrTrash/approval-testing-power
https://github.com/emilybache/GildedRose-Refactoring-Kata
Unveiling the Power of Approval Testing
By Janina Nemec
Unveiling the Power of Approval Testing
- 90