we're justin dave @searls · 2015-05-01 · have some side effect get /hypothetical_feature do...
TRANSCRIPT
We're Justin & Dave (a.k.a @searls & @dmosher) Say [email protected]
testdouble/real-world-testing-video
go forth and github clone:
## background
## background
* ~~purposes of each type of test~~
## background
* ~~purposes of each type of test~~* ~~integration tests~~
## background
* ~~purposes of each type of test~~* ~~integration tests~~* ~~frameworks vs. TDD~~
## background
* ~~purposes of each type of test~~* ~~integration tests~~* ~~frameworks vs. TDD~~* a handful of situational tactics
## background
* ~~purposes of each type of test~~* ~~integration tests~~* ~~frameworks vs. TDD~~* a handful of situational tactics* using Jasmine
## background
* ~~purposes of each type of test~~* ~~integration tests~~* ~~frameworks vs. TDD~~* a handful of situational tactics* using Jasmine* generally applicable
## background
* ~~purposes of each type of test~~* ~~integration tests~~* ~~frameworks vs. TDD~~* a handful of situational tactics* using Jasmine* generally applicable
-ish
## background
* ~~purposes of each type of test~~* ~~integration tests~~* ~~frameworks vs. TDD~~* a handful of situational tactics* using Jasmine* generally applicable
-ish *ymmv*
## syntax
## syntax
### What we don't do
## syntax
### What we don't do
* use Jasmine's (RSpec-like) DSL
describe("Math", function(){ });
describe("Math", function(){ var subject, result; });
describe("Math", function(){ var subject, result; beforeEach(function(){ }); });
describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math(); }); });
describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math(); }); describe("#add", function(){ }); });
describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math(); }); describe("#add", function(){ beforeEach(function(){ }); }); });
describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math(); }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); }); });
describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math(); }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); it("adds", function(){ }); }); });
describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math(); }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); it("adds", function(){ expect(result).toEqual(9); }); }); });
## syntax
### What we don't do
* use Jasmine's (RSpec-like) DSL * test my JavaScript with JavaScript
## syntax
### What's the problem?
## syntax
### What's the problem?
* Jasmine DSL is not obvious
describe('thing', function(){});
describe('thing', function(){});
beforeEach(function(){});
describe('thing', function(){});
beforeEach(function(){});
afterEach(function(){});
describe('thing', function(){});
beforeEach(function(){});
afterEach(function(){});
it('does stuff', function(){});
describe('thing', function(){});
beforeEach(function(){});
afterEach(function(){});
it('does stuff', function(){});
expect(true).toBeTruthy();
describe('thing', function(){});
beforeEach(function(){});
afterEach(function(){});
it('does stuff', function(){});
expect(true).toBeTruthy();
this.addMatchers({});
describe('thing', function(){});
beforeEach(function(){});
afterEach(function(){});
it('does stuff', function(){});
expect(true).toBeTruthy();
this.addMatchers({});
jasmine.createSpy().andCallThrough();
## syntax
### What's the problem?
* Jasmine DSL is not obvious * test code can be verbose, unwieldy
... expect(spec).toFinallyEnd(); }); }); }); }); }); });
## syntax
### What's the problem?
* Jasmine DSL is not obvious * test code can be verbose, unwieldy * those crying mustaches
## syntax
### What's the problem?
* Jasmine DSL is not obvious * test code can be verbose, unwieldy * those crying mustaches });
## syntax
### What we do
## syntax
### What we do
* write specs in CoffeeScript
describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math(); }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); it("adds", function(){ expect(result).toEqual(9); }); }); });
describe "Math", -> beforeEach -> @subject = new Math()
describe "#add", -> beforeEach -> @result = @subject.add(4,5)
it "adds", -> expect(@result).toEqual(9)
CoffeeScript basics*
CoffeeScript basics*
*Fear not, it's just JS.
var add = function(a,b) { return a + b; };
add = (a,b) -> a + b
this.save();
@save()
var self = this; save(function(){ self.display("Yay!"); });
save => @display("Yay!")
## syntax
### What we do
* write specs in CoffeeScript * use the *-given DSL
describe "Math", -> beforeEach -> @subject = new Math()
describe "#add", -> beforeEach -> @result = @subject.add(4,5)
it "adds", -> expect(@result).toEqual(9)
describe "Math", -> Given -> @subject = new Math()
describe "#add", -> When -> @result = @subject.add(4,5) Then -> @result == 9
describe("Math", function(){ var subject, result; beforeEach(function(){ subject = new Math(); }); describe("#add", function(){ beforeEach(function(){ result = subject.add(4,5); }); it("adds", function(){ expect(result).toEqual(9); }); }); });
describe "Math", -> Given -> @subject = new Math()
describe "#add", -> When -> @result = @subject.add(4,5) Then -> @result == 9
## syntax
### What we do
* write specs in CoffeeScript * use the *-given DSL * jasmine-given is a port of rspec-given
## syntax
### What we do
* write specs in CoffeeScript * use the *-given DSL * jasmine-given is a port of rspec-given * mocha-given is a port of jasmine-given
## syntax
### What we do
* write specs in CoffeeScript * use the *-given DSL * jasmine-given is a port of rspec-given * mocha-given is a port of jasmine-given * mocha-gwt is a rewrite of mocha-given
demo
Roman Numerals
1-standalone
how'd that go?
break
discovery testing
Discovery Tests
</>
I ❤ test doubles
I ❤ mocks
I 💔 other people's mocks
Hypothetical feature:
Hypothetical feature:
• Generate a random arithmetic problem (e.g. 5+3)
Hypothetical feature:
• Generate a random arithmetic problem (e.g. 5+3)
• Store it so that a subsequent request can find it
Hypothetical feature:
• Generate a random arithmetic problem (e.g. 5+3)
• Store it so that a subsequent request can find it
• Accept attempted solutions to problems and respond
{
{ id: 1,
{ id: 1, operands: {
{ id: 1, operands: { left: 5,
{ id: 1, operands: { left: 5, right: 8
{ id: 1, operands: { left: 5, right: 8 },
{ id: 1, operands: { left: 5, right: 8 }, operator: '+',
{ id: 1, operands: { left: 5, right: 8 }, operator: '+', description: '5 * 8'
{ id: 1, operands: { left: 5, right: 8 }, operator: '+', description: '5 * 8'}
{ id: 1, operands: { left: 5, right: 8 }, operator: '+', description: '5 * 8'}
{ id: 1, operands: { left: 5, right: 8 }, operator: '+', description: '5 * 8'}
{ id: 1, operands: { left: 5, right: 8 }, operator: '+', description: '5 * 8'}
GET /problemGets
Random Problem
Generates Random Problem
GET /problemGets
Random Problem
Generates Random Problem
GET /problemGets
Random Problem
Saves Problem
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
let's play
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
Focus
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
Leaf Nodes
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
No mocks, only logic!
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
Collaborators
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
No logic, only mocks!
Generates Random Problem
Describes Problem
GET /problemGets
Random Problem
Saves Problem
Gives Random Character
Gives Random Integer
too many things! cognitive overhead!
units so small they're disposable
Responding to changing requirements:
Have some side effect
GET /hypothetical_featureDo hard thing
Transform stuff
Query for stuff
Join Things
Build query
Filter Results
Contact Server
Paginate Stuff
Fetch stuff
Have some side effect
GET /hypothetical_featureDo hard thing
Transform stuff
Query for stuff
Join Things
Build query
Filter Results
Contact Server
Paginate Stuff
Fetch stuff1. Identify smallest sub-tree
affected by change
Have some side effect
GET /hypothetical_featureDo hard thing
Transform stuff
Query for stuff
Join Things
Build query
Filter Results
Contact Server
Paginate Stuff
Fetch stuff1. Identify smallest sub-tree
affected by change
Have some side effect
GET /hypothetical_featureDo hard thing
Transform stuff
Query for stuff
Join Things
Build query
Filter Results
Contact Server
Paginate Stuff
Fetch stuff1. Identify smallest sub-tree
affected by change
2. Delete sub-tree if change is non-trivial
Have some side effect
GET /hypothetical_featureDo hard thing
Transform stuff
Query for stuff
Join Things
Build query
Filter Results
1. Identify smallest sub-tree affected by change
2. Delete sub-tree if change is non-trivial
Have some side effect
GET /hypothetical_featureDo hard thing
Transform stuff
Query for stuff
Join Things
Build query
Filter Results
1. Identify smallest sub-tree affected by change
2. Delete sub-tree if change is non-trivial
3. Drive out a new solution
Have some side effect
GET /hypothetical_featureDo hard thing
Transform stuff
Query for stuff
Join Things
Build query
Filter Results
1. Identify smallest sub-tree affected by change
2. Delete sub-tree if change is non-trivial
3. Drive out a new solutionContact Server
Paginate better
Fetch stuff better
Have some side effect
GET /hypothetical_featureDo hard thing
Transform stuff
Query for stuff
Join Things
Build query
Filter Results
1. Identify smallest sub-tree affected by change
2. Delete sub-tree if change is non-trivial
3. Drive out a new solution
4. Pay technical debt by trusting future-us to know better
Contact Server
Paginate better
Fetch stuff better
Changing code is hard.
Changing code is hard.big
Changing code is hard.
Changing code is hard.long
Changing code is hard.
Changing code is hard.old
Changing code is hard.
Changing code is hard.ALL
Changing code is hard.
Consider a workflow that disposes, rather than changes, existing code
2-node
{
{ operator: '+',
{ operator: '+', operands: {
{ operator: '+', operands: { left: 84,
{ operator: '+', operands: { left: 84, right: 92
{ operator: '+', operands: { left: 84, right: 92 },
{ operator: '+', operands: { left: 84, right: 92 }, description: '84 + 92',
{ operator: '+', operands: { left: 84, right: 92 }, description: '84 + 92', id: 9222
{ operator: '+', operands: { left: 84, right: 92 }, description: '84 + 92', id: 9222}
GET /problem
GET /problemGET /problem/:id
GET /problemGET /problem/:idPOST /solution
3-lineman
4-testium