Principles ofSolitary Unit Testing – by Joseph D. Purcell – Who am I?



Principles ofSolitary Unit Testing – by Joseph D. Purcell – Who am I?

1 5


principles-of-solitary-unit-testing

Presentation slides for the Principles of Solitary Unit Testing.

On Github josephdpurcell / principles-of-solitary-unit-testing

Palantir.net

Principles ofSolitary Unit Testing

– DrupalCon Barcelona 2015 –

by Joseph D. Purcell

Who am I?

Engineer at Palantir, a full service web agency based in Chicago

Who are you?

You know unit testing and want to be better at it.

Solitary unit testing facilitates better software design.

  • This is the thesis.
  • at the very least, learning what it is facilitates better decision making about unit testing strategy

Overview

  • Part I. Definition of Solitary Unit Test
  • Part II. Principles of Solitary Unit Testing
  • Part III. Solitary Unit Test Smells
  • Part IV. Motivations for Solitary Unit Testing

Part I. Definition of Solitary Unit Test

Problem: many definitions of what unit test is and you may not know solitary vs sociable.

What is a "unit test"?

"unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use. Intuitively, one can view a unit as the smallest testable part of an application."

wikipedia.com

WikiPedia is most often quoted; but really? that's the best we can do?
"[I]t's a situational thing - the team decides what makes sense to be a unit for the purposes of their understanding of the system and its testing."

— Martin Fowler, martinfowler.com

This does get at the heart of how to approach it, but it's nothing you can build definitions on.

A unit test verifies a function's correctness against a specification.

  • it meets all interpretations
  • "verify" is true to roots: formal verification
  • function = constructor, method, or function
  • there is nothing you can unit test that could not be described by this definition
  • @todo is there a better word than "function"? maybe subroutine?

There are two problems with unit testing:

indirect inputs (shared state) indirect outputs (side effects)
  • shared state: globals, env vars, db, fs, net
  • side effects: collaborators, db, fs, net

The solution: solitary unit test.

  • The "solitary" allows you to isolate and control indirect inputs and outputs.

Solitary vs Sociable

The two aspects of a solitary unit test:

Never cross boundaries The Class Under Test should be the only concrete class found in a test

— Jay Fields, "Working Effectively With Unit Tests"

All other unit tests are sociable.

A boundary is "a database, a queue, another system, or even an ordinary class if that class is 'outside' the area your trying to work with or are responsible for"

— William E. Caputo, www.williamcaputo.com

  • @todo how to add a see also to Michael C. Feathers "Working Effectively With Legacy Code" concept of "Seams"

A boundary is an indirect input or output.

  • i.e. shared state or side effects
  • @todo validate this is a good def

Test Doubles

Dummy never called Fake is called Stub provide indirect inputs Spy (optionally) provide indirect inputs, capture indirect outputs Mock (optionally) provide indirect inputs, verify indirect outputs
  • These are the tools to deal with the boundary.

What is a good solitary unit test?

  • @todo consider properties of good unit test "Art of Unit Testing" Roy Osherove pg 6
  • @todo consider pillars of good tests "Art of Unit Testing" Roy Osherove pg 171

A good solitary unit test gains ROI where a sociable test would not.

  • The principles we are about to look at will help you evaluate ROI.
"Proving that code is correct is impractical. Testing does not guarantee correctness. However, good testing can minimize risk."

— Robert C. Martin "Clean Code"

The advantage of solitary unit testing is that the haystack is now a cotton ball.

Part II. Principles of Solitary Unit Testing

So, now we know that solitary unit testing is awesome, how do we do it?

Dummy

              
  $dummy = $this->getMockBuilder('SomeClass')->getMock();
  $cut = new ClassUnderTest($dummy);

  $response = $cut->methodDoesntNeedDummy();

  // assert something about the $response
              
            

... or just use null?

  • Assuming this doesn't get called.

Fake

              
  $fs = new FakeFileSystem();
  $cut = new ClassUnderTest($fs);

  $response = $cut->write('A string going to a log.');

  // assert something about the $response
              
            
  • Guzzle is a good example.

Stub

              
  $stub = $this->getMockBuilder('User')->getMock();
  $stub->method('getFirstName')->willReturn('First');
  $stub->method('getLastName')->willReturn('Last');
  $cut = new ClassUnderTest($stub);

  $fullName = $cut->getFullName();

  $this->assertEquals('First Last', $fullName);
              
            
  • Assuming this doesn't get called.

Spy

$user = $this->prophet->prophesize('User'); $cut = new ClassUnderTest($user->reveal()); $cut->getFullName(); $user->getFirstName()->shouldHaveBeenCalled(); $user->getLastName()->shouldHaveBeenCalled();

Mock

              
  $stub = $this->getMockBuilder('User')->getMock();
  $stub->expects($this->once())->method('getFirstName')->willReturn('First');
  $stub->expects($this->once())->method('getLastName')->willReturn('Last');
  $cut = new ClassUnderTest($stub);

  $fullName = $cut->getFullName();

  $this->assertEquals('First Last', $fullName);
              
            

Part III. Solitary Unit Test Smells

Assertion roullette

Easy to do b/c mixing state + behavior verification Solution: one assert per test Solution: assert last

Fragile Test

test fails when SUT is changed in ways that do not affect the part the test is exercising Easy to do when there are side effects; but, this would happen anyway

Obscure Test

difficult to understand the test at a glance Easy to do when using lots of mocks; it's hard to tell what is getting called! Solution: improve our testing frameworks Solution: use ObjectMother or DataBuilder Solution: use custom assertions Solution: expect literals Solution: avoid use of setUp; use inline setup

Test Code Duplication

the same test code is repeated many times Easy to do when needing to build complex test doubles or state. Solution: Replace ObjectMother with DataBuilder

High Test Maintaenance Cost

too much effort is spent maintaining tests Solution: avoid implementation overspecification Solution: remove negative or low ROI tests @todo list types of tests to remove from "Why you are wasting your time with unit testing"
"If it stinks, change it."

— Grandma Beck, discussing child-rearing philosophy, "Refactoring"

Part IV. Motivations for Solitary Unit Testing

Solitary unit testing enables higher code coverage.

Solitary unit testing enables more swim lanes.

You can test code that doesn't exist or is changing, as long as the interface doesn't change.

@todo change word from swim lane to something else

Solitary unit testing is faster than sociable unit testing.

"Writing unit tests is reinventing functional programming in non-functional languages."

— Christian S., noss.github.io

Yes, we do away with indirect inputs and outputs; but, we don't do away with the benefits of OO.
Pure functions still need tests.
"It should not be underestimated how much easier these functions are to test than their state-filled counterparts."

— Simon Honeywell, "Functional Programming in PHP"

Thank You!

References

"Unit Testing", WikiPedia "UnitTest", Martin Fowler "Working Effectively With Unit Tests", Jay Fields "TDD Pattern: Do not cross boundaries", William E. Caputo "Clean Code", Robert C. Martin "Refactoring", Martin Fowler "Writing unit tests is reinventing functional programming in non-functional languages", Christian S. "Functional Programming in PHP", Simon Honeywell

Further Reading

See list of references. "Object Mother", Martin Fowler "Test Data Builders: an alternative to the Object Mother pattern", Nat Pryce "Mocks Aren't Stubs", Martin Fowler "Google testing blog" "Improving developers enthusiasm for unit tests, using bubble charts", Jonathan Andrew Wolter "Why Most Unit Testing is Waste", James O Coplien "Don't Mock Concrete Classes", Steve Shogren "XUnit Test Patterns", George Mezaros "Working Effectively with Legacy Code", Michael C. Feathers "The Principles of OOD", Robert C. Martin "Object-Oriented Programming Revisited", Mike Bland "Goto Fail, Heartbleed, and Unit Testing Culture", Martin Fowler "How to Prevent the next Heartbleed", David A. Wheeler "How Is Critical 'Life or Death' Software Tested?", Michael Byrne
Principles ofSolitary Unit Testing – DrupalCon Barcelona 2015 – by Joseph D. Purcell