41
How to Test Asynchronous Code by Felix Geisendörfer 19.05.2011 (v2)

How to Test Asynchronous Code (v2)

Embed Size (px)

DESCRIPTION

Slides from my unit testing talk at JSBerlin on May 19.

Citation preview

Page 1: How to Test Asynchronous Code (v2)

How to Test Asynchronous Code

by Felix Geisendörfer

19.05.2011 (v2)

Page 2: How to Test Asynchronous Code (v2)

@felixge

Twitter / GitHub / IRC

Page 3: How to Test Asynchronous Code (v2)

Core Contributor

&

Module Author

node-mysql node-formidable

- Joined the mailing list in June 26, 2009- When I joined there where #24 people- Now mailing list has close to 4000 members members- First patch in September 2009

Page 4: How to Test Asynchronous Code (v2)

How to Test Asynchronous

Code

- Disclaimer: Don’t listen to me : )

Page 5: How to Test Asynchronous Code (v2)

The current approach

test('something', function(done) { doSomethingAsync(function() { assert.equals(...);

done(); });});

Page 6: How to Test Asynchronous Code (v2)

The current approach

test('something', function(done) { doSomethingAsync(function() { assert.equals(...);

done(); });});

If you have multiple tests, how will exceptions be linked?

Page 7: How to Test Asynchronous Code (v2)

db.query('SELECT A', function() { db.query('SELECT B', function() { db.query('SELECT C', function() { db.query('SELECT D', function() { // WTF }); }); });});

- Who here is running node in production?- Raise hands if you have a test suite for your node.js projects- Raise hands if you are happy with it- But why is it so difficult?

Page 8: How to Test Asynchronous Code (v2)

test('something', function(done) { doSomethingAsync(function() { assert.equals(...);

done(); });});

F%$! that shit

Forget that shit

Page 9: How to Test Asynchronous Code (v2)

What can we do?

- We looked at asynchronous testing frameworks- We really wanted to do TDD- But everything we tried was hard

Page 10: How to Test Asynchronous Code (v2)

Take out the I/O

Page 11: How to Test Asynchronous Code (v2)

Stub / Mock Dependencies

Page 12: How to Test Asynchronous Code (v2)

Synchronous Tests

Page 13: How to Test Asynchronous Code (v2)

Unit TestsIf you have nested callbacks (or any other I/O) in your tests, you are not unit testing

Page 14: How to Test Asynchronous Code (v2)

Microtestshttp://anarchycreek.com/2009/05/20/theyre-called-microtests/

• It is short, typically under a dozen lines of code.

• It is always automated.

• It does not test the object inside the running app, but instead in a purpose-built testing application.

• It invokes only a tiny portion of the code, most usually a single branch of a single function.

• It is written gray-box, i.e. it reads as if it were black-box, but sometimes takes advantage of white-box knowledge. (Typically a critical factor in avoiding combinatoric issues.)

• It is coded to the same standard as shipping code, i.e. the team’s best current understanding of coding excellence.

• It is vault-committed source, with a lifetime co-terminous with the functionality it tests.

• In combination with all other microtests of an app, it serves as a ‘gateway-to-commit’. That is, a developer is encouraged to commit anytime all microtests run green, and discouraged (strongly, even nastily) to commit otherwise.

• It takes complete control of the object-under-test and is therefore self-contained, i.e. running with no dependencies on anything other than the testing code and its dependency graph.

• It runs in an extremely short time, milliseconds per test.

• It provides precise feedback on any errors that it encounters.

• It usually (not always) runs entirely inside a single computer.

• It usually (not always) runs entirely inside a single process, i.e. with few extra-process runtime dependencies.

• It is part of a collection all or any subset of which is invokable with a single programmer gesture.

• It is written before the code-change it is meant to test.

• It avoids most or all usage of ‘awkward’ collaborators via a variety of slip-and-fake techniques.

• It rarely involves construction of more than a few classes of object, often one or two, usually under five.

Page 15: How to Test Asynchronous Code (v2)

Approach

• Load SUT in a Sandbox using v8 / node.js

• Inject dependencies using the global object of the sandbox

• Don’t use test doubles for internals, only for peers

Internal: An object that has the same life span as its hostPeers: Something that is being passed in / out of the SUT

Page 16: How to Test Asynchronous Code (v2)

Test Doubles

• Dummy Object

• Test Stub

• Test Spy

• Mock Object

• Fake Object

• Temporary Test Stub

http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html

Page 17: How to Test Asynchronous Code (v2)

node-fake

Page 18: How to Test Asynchronous Code (v2)

node-fake

var fake = require('fake')();var object = {};

fake.expect(object, 'method');

object.method();

Page 19: How to Test Asynchronous Code (v2)

node-fake

var fake = require('fake')();var object = {};

var objectMethodCall = fake.stub(object, 'method');

object.method();

assert.equals(objectMethodCall.calls.length, 1);

Page 20: How to Test Asynchronous Code (v2)

node-fake

var fake = require('fake')();

var MyClass = fake.class('MyClass');fake .expect('new', MyClass) .withArgs(1, 2);

var myClass = new MyClass(1, 2);

Page 21: How to Test Asynchronous Code (v2)

node-microtest

Page 22: How to Test Asynchronous Code (v2)

node-microtest

var fs = require('fs');

module.exports = function(path) { var file = fs.createReadStream(path); file.pipe(process.stdout);};

cat.js

Page 23: How to Test Asynchronous Code (v2)

node-microtest

var test = require('microtest').module('cat.js');

test.requires('fs');test.context.process = { stdout: test.object('stdout'),};

var cat = test.compile();

test-cat.js

Page 24: How to Test Asynchronous Code (v2)

node-microtesttest.describe('cat', function() { var PATH = test.value('path'); var file = test.object('file');

test .expect(test.required.fs, 'createReadStream') .withArgs(PATH) .andReturn(file);

test .expect(file, 'pipe') .withArgs(test.context.process.stdout);

cat(PATH);});

test-cat.js

Page 25: How to Test Asynchronous Code (v2)

Benefits

We take the position that the real benefit of extensive microtest-driven development isn't higher quality at all. Higher quality is a side effect of TDD. Rather, the benefit and real purpose of TDD as we teach it is sheer productivity: more function faster.

Mike Hill

Page 26: How to Test Asynchronous Code (v2)

Benefits

• Simpler implementations

• Short change / verification cycles

• Tests for edge cases (error handling, race conditions, etc.)

Page 27: How to Test Asynchronous Code (v2)

There really are only two acceptable models of development: "think and analyze" or "years and years of testing on thousands of machines".

Linus Torvalds

Page 28: How to Test Asynchronous Code (v2)

Disadvantages

• Requires gray / white box knowledge of implementation

• Lots of test code needs to be written

• Sometimes feels awkward

http://martinfowler.com/articles/mocksArentStubs.html#ClassicalAndMockistTesting

Page 29: How to Test Asynchronous Code (v2)

Numbers

Page 30: How to Test Asynchronous Code (v2)

node-mysql

Page 31: How to Test Asynchronous Code (v2)

node-mysql

0

425

850

1.275

1.700

library tests

Lines of code

library vs test code: 1x : 1.35x

Library: 1240 LoC (+600 LoC MySql constants)Tests:Tests: 1673 LoC

Page 32: How to Test Asynchronous Code (v2)

node-mysql

0

325

650

975

1.300

integration micro

integration vs micro tests: 1x : 2.89x

Lines of code

Micro Tests: 1243 LoCIntegration tests: 430 LoC

Page 33: How to Test Asynchronous Code (v2)

node-mysql

0

100

200

300

400

integration micro

Assertions

integration vs micro tests: 1x : 8.15x

Micro Tests: 375 assertsIntegration tests: 46 asserts

Page 34: How to Test Asynchronous Code (v2)

Transloadit

Page 35: How to Test Asynchronous Code (v2)

Transloadit

0

3.250

6.500

9.750

13.000

library tests

Lines of code

library vs test code: 1x : 2.04x

Library: 6184 LoCTests: 12622 LoC

Not included: Fixture data, server configuration, customer website, dependencies we developed

Page 36: How to Test Asynchronous Code (v2)

Transloadit

0

2.500

5.000

7.500

10.000

integration micro

integration vs. micro tests: 1x : 4.30x

Lines of code

Integration tests: 2254 LoCMicro Tests: 9695 LoC

Page 37: How to Test Asynchronous Code (v2)

Transloadit

0

750

1.500

2.250

3.000

integration micro

Assertions

integration vs. micro tests: 1x : 12.30x

Integration tests: 189 assertsMicro Tests: 2324 asserts

Page 38: How to Test Asynchronous Code (v2)

tl;dr

• Don’t use integration tests to show the basic correctness of your software.

• Write more microtests.

Page 39: How to Test Asynchronous Code (v2)

Questions?

☝Thank You

Page 40: How to Test Asynchronous Code (v2)

Thank You

Page 41: How to Test Asynchronous Code (v2)

Node.js Workshop Cologne

nodecologne.eventbrite.com

Use discount code ‘berlinjs’ for 10% off