Successfully reported this slideshow.

How to Test Asynchronous Code (v2)

11

Share

Loading in …3
×
1 of 41
1 of 41

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

How to Test Asynchronous Code (v2)

  1. 1. How to Test Asynchronous Code by Felix Geisendörfer 19.05.2011 (v2)
  2. 2. @felixge Twitter / GitHub / IRC
  3. 3. 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
  4. 4. - Disclaimer: Don’t listen to me : )
  5. 5. The current approach test('something', function(done) { doSomethingAsync(function() { assert.equals(...); done(); }); });
  6. 6. The current approach test('something', function(done) { doSomethingAsync(function() { assert.equals(...); done(); }); }); If you have multiple tests, how will exceptions be linked?
  7. 7. 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?
  8. 8. test('something', function(done) { doSomethingAsync(function() { assert.equals(...); done(); }); }); F%$! that shit Forget that shit
  9. 9. - We looked at asynchronous testing frameworks - We really wanted to do TDD - But everything we tried was hard
  10. 10. Take out the I/O
  11. 11. Stub / Mock Dependencies
  12. 12. Synchronous Tests
  13. 13. Unit Tests If you have nested callbacks (or any other I/O) in your tests, you are not unit testing
  14. 14. Microtests http://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.
  15. 15. 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 host Peers: Something that is being passed in / out of the SUT
  16. 16. Test Doubles • Dummy Object • Test Stub • Test Spy • Mock Object • Fake Object • Temporary Test Stub http://xunitpatterns.com/Mocks,%20Fakes,%20Stubs%20and%20Dummies.html
  17. 17. node-fake var fake = require('fake')(); var object = {}; fake.expect(object, 'method'); object.method();
  18. 18. node-fake var fake = require('fake')(); var object = {}; var objectMethodCall = fake.stub(object, 'method'); object.method(); assert.equals(objectMethodCall.calls.length, 1);
  19. 19. node-fake var fake = require('fake')(); var MyClass = fake.class('MyClass'); fake .expect('new', MyClass) .withArgs(1, 2); var myClass = new MyClass(1, 2);
  20. 20. node-microtest var fs = require('fs'); module.exports = function(path) { var file = fs.createReadStream(path); file.pipe(process.stdout); }; cat.js
  21. 21. 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
  22. 22. node-microtest test.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
  23. 23. 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
  24. 24. Benefits • Simpler implementations • Short change / verification cycles • Tests for edge cases (error handling, race conditions, etc.)
  25. 25. There really are only two acceptable models of development: "think and analyze" or "years and years of testing on thousands of machines". Linus Torvalds
  26. 26. 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
  27. 27. node-mysql Lines of code 1.700 1.275 850 425 0 library tests library vs test code: 1x : 1.35x Library: 1240 LoC (+600 LoC MySql constants)Tests: Tests: 1673 LoC
  28. 28. node-mysql Lines of code 1.300 975 650 325 0 integration micro integration vs micro tests: 1x : 2.89x Micro Tests: 1243 LoC Integration tests: 430 LoC
  29. 29. node-mysql Assertions 400 300 200 100 0 integration micro integration vs micro tests: 1x : 8.15x Micro Tests: 375 asserts Integration tests: 46 asserts
  30. 30. Transloadit Lines of code 13.000 9.750 6.500 3.250 0 library tests library vs test code: 1x : 2.04x Library: 6184 LoC Tests: 12622 LoC Not included: Fixture data, server configuration, customer website, dependencies we developed
  31. 31. Transloadit Lines of code 10.000 7.500 5.000 2.500 0 integration micro integration vs. micro tests: 1x : 4.30x Integration tests: 2254 LoC Micro Tests: 9695 LoC
  32. 32. Transloadit Assertions 3.000 2.250 1.500 750 0 integration micro integration vs. micro tests: 1x : 12.30x Integration tests: 189 asserts Micro Tests: 2324 asserts
  33. 33. tl;dr • Don’t use integration tests to show the basic correctness of your software. • Write more microtests.
  34. 34. Questions?
  35. 35. Node.js Workshop Cologne nodecologne.eventbrite.com Use discount code ‘berlinjs’ for 10% off

×