Designing Testable Software

Preview:

Citation preview

Designing Testable SoftwareOpKoKo 16.2

Ekerö October 21, 2016

@DanielDeogun

@DanielDeogun

About Me

Daniel Deogun Coder and Quality Defender

- VP Academy, Core 3, Stockholm

- Current assignment Hi3G / Nordic Choice Hotels

- Speaker, Teacher, Lead Developer

- Author of Secure by Design, Manning publ (in progress)

- Interests: DDD, DDSec, TDD, BDD, DbC, …

@DanielDeogun

Conclusion

- Put the test hat on when designing a system

- Testing is quite hard and require a lot of good design

- You get pretty far by thinking test first

@DanielDeogun

The Spec of the New “System”

Easy to add / remove / update functionality

Have same functionality as the “old” system

Must be resilient

Easy to maintain

@DanielDeogun

Where do you begin?

[1]

@DanielDeogun

Test Hat On

Let’s put the test hat on

and see what we need…[2]

@DanielDeogun

Requirement

Have same functionality as the “old” system

@DanielDeogun

Requirement

How do we verify this?

Have same functionality as the “old” system

@DanielDeogun

Requirement

How do we verify this?

Is there any documentation?

Have same functionality as the “old” system

@DanielDeogun

Requirement

How do we verify this?

Is there any documentation?

How do we report progress?

Have same functionality as the “old” system

@DanielDeogun

Requirement

How do we verify this?

Is there any documentation?

Is there a domain expert?How do we report progress?

Have same functionality as the “old” system

@DanielDeogun

Requirement

How do we verify this?

Is there any documentation?

Is there a domain expert?

Do we want “exactly” the same behavior?

How do we report progress?

Have same functionality as the “old” system

@DanielDeogun

Behavior Driven Development in a Nutshell

BDD tries to capture the behavior of a system, not how it’s implemented

@DanielDeogun

BDD in Practice

Given … When … Then …

scenarios

produce

Gherkin notation

@DanielDeogun

BDD in Practice Scenarios Act As a Client

Given … When … Then …

public class Scenario {

public void given() {…}

public void when() {…}

public void then() {…}

}

convert to

executable test acting as a client

invokes

application

@DanielDeogun

Application Behavior Captured by Scenario Tests

BDD Scenarios

application

@DanielDeogun

Application Behavior Captured by Scenario Tests

BDD Scenarios

But what about dependencies to other systems?

invokes

application

@DanielDeogun

Application Behavior Captured by Scenario Tests

BDD Scenarios

But what about dependencies to other systems?

invokes

application

Let’s have a look at 3 important principles…

@DanielDeogun

Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Abstractions should not depend on details. Details should depend on abstractions.

- https://en.wikipedia.org/wiki/Dependency_inversion_principle

@DanielDeogun

Dependency Inversion Principle (DIP)

Dependency

Dependency implementation

abstraction

@DanielDeogun

Dependency Injection (DI) Explained

public class Monkey {private final Banana banana;

public Monkey() { this.banana = new Banana();}…

}

Monkey depends on Banana but the Monkey creates the banana.

public class Monkey {private final Banana banana;

public Monkey(final Banana banana) { this.banana = notNull(banana);}…

}

Monkey depends on Banana but banana is given (injected) to Monkey

@DanielDeogun

Liskov’s Substitution Principle

Let ϕ(x) be a property provable about objects x of type T. Then ϕ(y) should be true for objects y of type S where S is a subtype of T.

- Barbara Liskov and Jeannette Wing [3]

@DanielDeogun

LSP - “For Dummies”

If C is a subtype of P, then objects of type P may be replaced by objects of type C without violating behavior or invariants

- Common Sense

class C extends P {…}

final P p = new C();

@DanielDeogun

The Beautiful Trinity

DIP + DI + LSP = True

[6]

@DanielDeogun

DIP, DI, and LSP Allows Isolated Testing

Let’s use DI to inject dependencies

Let’s use DIP to remove dependencies to low level implementations

Let’s use LSP to ensure invariants and behavior

dependency

abstraction

@DanielDeogun

Pros & Cons BDD & Spec by Example

+ Easy to verify business rules and expected behavior + A good way to learn how the system works + Creates confidence

- Deciding what to test may be hard - Feature driven scenario organization may create duplicated tests - Requires deep domain knowledge

@DanielDeogun

Evaluation

How do we verify this?

Is there any documentation?

Is there a domain expert?

Do we want “exactly” the same behavior?

How do we report progress?

Have same functionality as the “old” system

@DanielDeogun

Evaluation

How do we verify this?

Is there any documentation?

Is there a domain expert?

Do we want “exactly” the same behavior?

How do we report progress?

Have same functionality as the “old” system

@DanielDeogun

Evaluation

How do we verify this?

Is there any documentation?

Is there a domain expert?

Do we want “exactly” the same behavior?

How do we report progress?

√?Have same functionality as the “old” system

@DanielDeogun

Evaluation

How do we verify this?

Is there any documentation?

Is there a domain expert?

Do we want “exactly” the same behavior?

How do we report progress?

√?Have same functionality as the “old” system

@DanielDeogun

Evaluation

How do we verify this?

Is there any documentation?

Is there a domain expert?

Do we want “exactly” the same behavior?

How do we report progress?

√?√

√?Have same functionality as the “old” system

@DanielDeogun

Evaluation

How do we verify this?

Is there any documentation?

Is there a domain expert?

Do we want “exactly” the same behavior?

How do we report progress?

?√

√?Have same functionality as the “old” system

@DanielDeogun

Requirement

Easy to add / remove / update functionality

Easy to maintain

@DanielDeogun

Requirement

Easy to add / remove / update functionality

How do we avoid breaking things?

Easy to maintain

@DanielDeogun

Requirement

Easy to add / remove / update functionality

How do we avoid breaking things?

How do we design for change?

Easy to maintain

@DanielDeogun

Requirement

Easy to add / remove / update functionality

How do we avoid breaking things?

How do we design for change?

Easy to maintain

What does easy to maintain mean?

@DanielDeogun

Test Driven Development

RED

GREEN

REFACTOR

Prove need

Smallest code change

Improve

TDD tend to focus on how something is implemented rather than its behavior

A good practice is to use BDD when doing TDD

@DanielDeogun

TDD Á la BDD

public void test_ticker() {Ticker ticker = new Ticker();

ticker.increment();

assertEquals(1, ticker.value());}

TDD

@DanielDeogun

TDD Á la BDD

public void test_ticker() {Ticker ticker = new Ticker();

ticker.increment();

assertEquals(1, ticker.value());}

TDDpublic void should_increment_ticker() {

givenTickerWithRandomStart();

int expectedValue = ticker.value() + ticker.incrementStep();

ticker.increment();

thenTickerIsIncrementedTo(expectedValue);}

TDD á la BDD

@DanielDeogun

TDD Á la BDD

public void test_ticker() {Ticker ticker = new Ticker();

ticker.increment();

assertEquals(1, ticker.value());}

!- Never verify more than one behavior in each test - Don’t use a common set up method

TDDpublic void should_increment_ticker() {

givenTickerWithRandomStart();

int expectedValue = ticker.value() + ticker.incrementStep();

ticker.increment();

thenTickerIsIncrementedTo(expectedValue);}

TDD á la BDD

@DanielDeogun

Mocks

“…mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other object, …”

https://en.wikipedia.org/wiki/Mock_object

@DanielDeogun

Use DI to Inject Mocksprivate final List<Item> items = mock(List.class); private final ShoppingCart shoppingCart = new ShoppingCart(items);

public class ShoppingCart { private final List<Item> items; public ShoppingCart(final List<Item> items) { this.items = notNull(items); } public boolean add(final Item item) { return items.add(notNull(item)); } public boolean remove(final Item item) { return items.remove(notNull(item)); } …

@DanielDeogun

The Great Frustration

Incorrect mocking makes you spend more time updating test code than production code

And the reason is…

@DanielDeogun

The Great Frustration

Incorrect mocking makes you spend more time updating test code than production code

And the reason is…

Nobody puts Barbra in the corner!

@DanielDeogun

Violating LSP Yield Brittle Tests

private final List<Item> items = mock(List.class); private final ShoppingCart shoppingCart = new ShoppingCart(items);

public class ShoppingCart { private final List<Item> items; public ShoppingCart(final List<Item> items) { this.items = notNull(items); } public boolean add(final Item item) { return items.add(notNull(item)); } public boolean remove(final Item item) { return items.remove(notNull(item)); } …

@DanielDeogun

Violating LSP Yield Brittle Tests

@Test public void should_remove_item() { final Item coke = new Coke(); BDDMockito.given(items.remove(coke)).willReturn(true); final boolean result = shoppingCart.remove(coke); assertTrue(result); }

public class ShoppingCart { private final List<Item> items; public ShoppingCart(final List<Item> items) { this.items = notNull(items); } public boolean remove(final Item item) {

return items.remove(notNull(item)); } …

@DanielDeogun

Violating LSP Yield Brittle Tests

@Test public void should_remove_item() { final Item coke = new Coke(); BDDMockito.given(items.remove(coke)).willReturn(true); final boolean result = shoppingCart.remove(coke); assertTrue(result); }

public class ShoppingCart { private final List<Item> items; public ShoppingCart(final List<Item> items) { this.items = notNull(items); } public boolean remove(final Item item) {

return items.remove(notNull(item)); } …

public class ShoppingCart { private final List<Item> items; public ShoppingCart(final List<Item> items) { this.items = notNull(items); } public boolean remove(final Item item) { return items.removeAll(items.stream() .filter(i -> i.equals(item)) .collect(toList())); }

@DanielDeogun

Violating LSP Yield Brittle Tests

@Test public void should_remove_item() { final Item coke = new Coke(); BDDMockito.given(items.remove(coke)).willReturn(true); final boolean result = shoppingCart.remove(coke); assertTrue(result); }

public class ShoppingCart { private final List<Item> items; public ShoppingCart(final List<Item> items) { this.items = notNull(items); } public boolean remove(final Item item) {

return items.remove(notNull(item)); } …

public class ShoppingCart { private final List<Item> items; public ShoppingCart(final List<Item> items) { this.items = notNull(items); } public boolean remove(final Item item) { return items.removeAll(items.stream() .filter(i -> i.equals(item)) .collect(toList())); }

java.lang.NullPointerException

@DanielDeogun

Analysis

The “items” mock doesn’t satisfy LSP

Invoking an unmocked method on the items object causes a failure

Beware: Mockito uses default mocking behavior on some types (e.g. List, int, boolean, etc)

private final List<Item> items = mock(List.class); private final ShoppingCart shoppingCart = new ShoppingCart(items);

@DanielDeogun

Tip of the Day

Make domain classes final to “prevent“ mocking![8]

org.mockito.exceptions.base.MockitoException: Cannot mock/spy class se.omegapoint.opkoko.MyDomainClassMockito cannot mock/spy following: - final classes - anonymous classes - primitive types

@DanielDeogun

Branch By Code Using Feature Toggles

http://martinfowler.com/articles/feature-toggles.html

@DanielDeogun

Branch By Code Using Feature Toggles

Release toggles • Compile time dependency • Ex: use DI and start app with different configuration

Ops toggles • Runtime dependency • Ex: special API only available for operations

Experiment toggles • Runtime dependency used for A/B testing • Ex: toggle is based on user information

Permission toggles • Runtime dependency • Ex: Toggle is based on payment information

@DanielDeogun

How Do Feature Toggles Affect Testing?

Release toggles • test what’s enabled (in production)

Ops toggles • make sure they only work for operations

Experiment toggles • test the selection algorithm

Permission toggles • test applicability

We cannot test every possible combination

Make an educated choice

@DanielDeogun

Evaluation

Easy to add / remove / update functionality

How do we avoid breaking things?

How do we design for change?

Easy to maintain

What does easy to maintain mean?

@DanielDeogun

Evaluation

Easy to add / remove / update functionality

How do we avoid breaking things?

How do we design for change?

Easy to maintain

What does easy to maintain mean?

@DanielDeogun

Evaluation

Easy to add / remove / update functionality

How do we avoid breaking things?

How do we design for change?

Easy to maintain

What does easy to maintain mean?

@DanielDeogun

Evaluation

Easy to add / remove / update functionality

How do we avoid breaking things?

How do we design for change?

Easy to maintain

What does easy to maintain mean?

√√?

@DanielDeogun

Requirement

Must be resilient

@DanielDeogun

Requirement

How do we measure “resilience?”

Must be resilient

@DanielDeogun

Requirement

How do we measure “resilience?”

How do we test resilience?

Must be resilient

@DanielDeogun

Requirement

How do we measure “resilience?”

How do we test resilience?

Can we find the “upper bound?”

Must be resilient

@DanielDeogun

System Integration Resilience

@DanielDeogun

Circuit Breaker

“A circuit breaker is an automatically operated electrical switch designed to protect an electrical circuit from damage caused by overcurrent or overload or short circuit.”

- https://en.wikipedia.org/wiki/Circuit_breaker

@DanielDeogun

Circuit Breakers Avoid Killing Backend

circuit breakers

@DanielDeogun

Schematic view of a Circuit Breaker

- Release It! Michael Nygard, The Pragmatic Bookshelf

@DanielDeogun

Bulkhead Pattern

https://github.com/Netflix/Hystrix/wiki/How-it-Works#Threads

[9]

@DanielDeogun

Separate Thread Pools Avoid Cascading Failures

circuit breakers

Separate thread pools

@DanielDeogun

Request Collapsing Pattern

https://github.com/Netflix/Hystrix/wiki/How-it-Works#RequestCollapsing

@DanielDeogun

“Internal” Resilience

[5]

Bad input data

Corrupt responses

Design by Contract

Message driven

Domain Driven DesignNull values

Default values

ImmutabilityConcurrency

@DanielDeogun

Hostile Environment

Imagine an environment whose only purpose is to bring your application to its knees

Each endpoint is a potential “threat”

Put your application under heavy load, inject bad input, return corrupt data, etc

Make this a stage in your delivery pipeline

Monitor memory consumption, response times, etc

@DanielDeogun

Evaluation

How do we measure “resilience?”

How do we test resilience?

Must be resilient

Can we find the “upper bound?”

@DanielDeogun

Evaluation

How do we measure “resilience?”

How do we test resilience?

Must be resilient

Can we find the “upper bound?”

√?

@DanielDeogun

Evaluation

How do we measure “resilience?”

How do we test resilience?

Must be resilient

Can we find the “upper bound?”

√?

@DanielDeogun

Evaluation

How do we measure “resilience?”

How do we test resilience?

Must be resilient

√Can we find the “upper bound?”

√?

@DanielDeogun

The Spec of the New “System”

Easy to add / remove / update functionality

Have same functionality as the “old” system

Must be resilient

Easy to maintain

@DanielDeogun

Conclusion

- Put the test hat on when designing a system

- Testing is quite hard and require a lot of good design

- You get pretty far by thinking test first

@DanielDeogun

Q & A

[7]

@DanielDeogun

Thanks@DanielDeogun

@DanielDeogun

References[1] Torsten, math teacher, https://flic.kr/p/ndFN4Q License: https://creativecommons.org/licenses/by/2.0/

[2] Emily Moe, IMG_7788, https://flic.kr/p/aH4rwk, License: https://creativecommons.org/licenses/by-nd/2.0/

[3] Liskov’s Substitution Principle, Wikipedia, https://en.wikipedia.org/wiki/Liskov_substitution_principle

[4] Tsutomu Takasu, Horse racing event, https://flic.kr/p/6Yy3NZ, License: https://creativecommons.org/licenses/by/2.0/

[5] http://upload.wikimedia.org/wikipedia/commons/thumb/1/1b/Emblem-evil-computer.svg/500px-Emblem-evil-computer.svg.png

[6] Huey, Dewey, and Louie Duck, drawn by cartoonist Carl Barks, https://en.wikipedia.org/wiki/File:Louie_Dewey_and_Huey.png

[7] Questions, https://flic.kr/p/9ksxQa] by Damián Navas, License: https://creativecommons.org/licenses/by-nc-nd/2.0/

[8] Chuck Coker, Light Bulb No. 1, https://flic.kr/p/66KLFn, License: https://creativecommons.org/licenses/by-nd/2.0/

[9] DRVMX, Titantic, https://flic.kr/p/gNLK84, License: https://creativecommons.org/licenses/by-nd/2.0/

Recommended