unit testing you ain’t doing it, and you should david cantrell...

57
Unit Testing You ain’t doing it, and you should David Cantrell [email protected] Chief Plumber, UK2 Ltd

Upload: prosper-brooks

Post on 16-Jan-2016

218 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Unit Testing

You ain’t doing it, and you should

David Cantrell [email protected]

Chief Plumber, UK2 Ltd

Page 2: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Unit Testing

You ain’t doing it, and you should

David Cantrell [email protected]

Chief Plumber, UK2 Ltd

12:20 <@dc> ees me, ees mario!12:21 <@ned> dc is promoted to chief plumber, in charge of princess rescue and mushroom eating

Boss

Page 3: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

This is your code

Page 4: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

This is your code

Page 5: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

This is your code

Page 6: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 7: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 8: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 9: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 10: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 11: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 12: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 13: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 14: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

Page 15: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s run a test

GET /foo/bar/baz

not ok 43572 GET /customer/94/invoice got right data

Page 16: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Where was that test failure?

Page 17: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Why do we test?

Page 18: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Why do we test?

•Testing new codedid I get this new feature right?

Page 19: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Why do we test?

•Testing new codedid I get this new feature right?

•Regression testingdid I break any existing code?

Page 20: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Why do we test?

•Testing new codedid I get this new feature right?

•Regression testingdid I break any existing code?

what did I break?

Page 21: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Why do we test?

•Testing new codedid I get this new feature right?

•Regression testingdid I break any existing code?

what did I break?

where?

Page 22: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

The solution (sort of)

Page 23: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

The solution (sort of)

Page 24: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

The solution (sort of)

Page 25: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

The solution (sort of)

and so on ad infinitum et tedium

Page 26: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

That solution sucks

Great, you just tested most ofyour code multiple times.

Page 27: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

That solution sucks

Great, you just tested most ofyour code multiple times.

Your tests took two hours.

Page 28: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

That solution sucks

Great, you just tested most ofyour code multiple times.

Your tests took two hours.

And again when you think you fixed them.

Page 29: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

But that wasn’t unit testing

Page 30: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

This is unit testing

Page 31: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

This is unit testing

Page 32: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

This is unit testing

Page 33: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

This is unit testing

Repeat ad infinitum et tedium

Page 34: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

This is unit testing

You just tested all ofyour code once!

Your tests took twenty minutes!

Page 35: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

That’s the end of the theory

Any questions?

Page 36: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s look at some code

Page 37: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s look at some code

(it doesn’t do much)

Page 38: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Let’s look at some code

Page 39: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

End to end testspackage Dancer::Example::Calculator::Add;

use strict;use warnings;use WWW::Google::Calculator;

sub get { my $class = shift; my @numbers = @_; die("illegal arguments") if( grep { $_ !~ /^-?\d+(\.\d+)?$/ } @numbers ); my $result = WWW::Google::Calculator-> new()->calc("$numbers[0] + $numbers[1]"); $result =~ s/.*= //; return $result;}

1;

use Dancer::Test;

...

response_status_is( [ GET => '/add/2/lemon'], 500, "GET /add/2/lemon - HTTP status is correct" ); response_status_is( [ GET => '/add/2/4'], 200, "GET /add/2/4 - HTTP status is correct" ); response_content_is( [ GET => '/add/2/4'], '6', "GET /add/2/4 - content is correct" );

Page 40: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

End to end testspackage Dancer::Example::Calculator::Add;

use strict;use warnings;use WWW::Google::Calculator;

sub get { my $class = shift; my @numbers = @_; die("illegal arguments") if( grep { $_ !~ /^-?\d+(\.\d+)?$/ } @numbers ); my $result = WWW::Google::Calculator-> new()->calc("$numbers[0] + $numbers[1]"); $result =~ s/.*= //; return $result;}

1;

use Dancer::Test;

...

SKIP: { skip "google isn't responding or its interface has changed", 3 unless(do { my $r = eval { WWW::Google::Calculator->new()->calc('1+1') }; defined($r) && $r eq '1 + 1 = 2'; });

response_status_is( [ GET => '/add/2/lemon'], 500, "GET /add/2/lemon - HTTP status is correct" ); response_status_is( [ GET => '/add/2/4'], 200, "GET /add/2/4 - HTTP status is correct" ); response_content_is( [ GET => '/add/2/4'], '6', "GET /add/2/4 - content is correct" );}

Page 41: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

End to end testspackage Dancer::Example::Calculator::Add;

use strict;use warnings;use WWW::Google::Calculator;

sub get { my $class = shift; my @numbers = @_; die("illegal arguments") if( grep { $_ !~ /^-?\d+(\.\d+)?$/ } @numbers ); my $result = WWW::Google::Calculator-> new()->calc("$numbers[0] + $numbers[1]"); $result =~ s/.*= //; return $result;}

1;

use Dancer::Test;

...

SKIP: { skip "google isn't responding or its interface has changed", 3 unless(do { my $r = eval { WWW::Google::Calculator->new()->calc('1+1') }; defined($r) && $r eq '1 + 1 = 2'; });

response_status_is( [ GET => '/add/2/lemon'], 500, "GET /add/2/lemon - HTTP status is correct" ); response_status_is( [ GET => '/add/2/4'], 200, "GET /add/2/4 - HTTP status is correct" ); response_content_is( [ GET => '/add/2/4'], '6', "GET /add/2/4 - content is correct" );}

$ time PERL5LIB=lib prove t/end-to-end.t t/end-to-end.t .. ok All tests successful.Files=1, Tests=3, 8 wallclock secs ...

Page 42: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Get rid of the front-end layerpackage Dancer::Example::Calculator::Add;

use strict;use warnings;use WWW::Google::Calculator;

sub get { my $class = shift; my @numbers = @_; die("illegal arguments") if( grep { $_ !~ /^-?\d+(\.\d+)?$/ } @numbers ); my $result = WWW::Google::Calculator->new()->calc("$numbers[0] + $numbers[1]"); $result =~ s/.*= //; return $result;}

1;throws_ok( sub { Dancer::Example::Calculator::Add->get(1, 'lemons') }, qr/illegal arguments/, "catches non-numeric args");...

is(Dancer::Example::Calculator::Add->get(2, 3), 5, "Add->get() works for a simple case");

# Test that the error thrown above doesn't accidentally catch# some legit datais(Dancer::Example::Calculator::Add->get(-4, 2), -2, "works for -ves too");is(Dancer::Example::Calculator::Add->get(0.2, 1), 1.2, "and decimals");is(Dancer::Example::Calculator::Add->get(1, -2.2), -1.2, "and -ve decimals");

Page 43: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Prepare to mock the call to Googlepackage Dancer::Example::Calculator::Add;

use strict;use warnings;use Class::Mockable _google_calculator => 'Dancer::Example::Calculator::Utils::GoogleInterface';use Dancer::Example::Calculator::Utils::GoogleInterface;

sub get { my $class = shift; my @numbers = @_; die("illegal arguments") if( grep { $_ !~ /^-?\d+(\.\d+)?$/ } @numbers ); my $result = _google_calculator->calc("$numbers[0] + $numbers[1]”);

return $result;}

1;throws_ok( sub { Dancer::Example::Calculator::Add->get(1, 'lemons') }, qr/illegal arguments/, "catches non-numeric args");...

is(Dancer::Example::Calculator::Add->get(2, 3), 5, "Add->get() works for a simple case");

# Test that the error thrown above doesn't accidentally catch# some legit datais(Dancer::Example::Calculator::Add->get(-4, 2), -2, "works for -ves too");is(Dancer::Example::Calculator::Add->get(0.2, 1), 1.2, "and decimals");is(Dancer::Example::Calculator::Add->get(1, -2.2), -1.2, "and -ve decimals");

Page 44: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Prepare to mock the call to Googlepackage Dancer::Example::Calculator::Add;

use strict;use warnings;use Class::Mockable _google_calculator => 'Dancer::Example::Calculator::Utils::GoogleInterface';use Dancer::Example::Calculator::Utils::GoogleInterface;

sub get { my $class = shift; my @numbers = @_; die("illegal arguments") if( grep { $_ !~ /^-?\d+(\.\d+)?$/ } @numbers ); my $result = _google_calculator->calc("$numbers[0] + $numbers[1]”);

return $result;}

1;

package Dancer::Example::Calculator::Utils::GoogleInterface;

use strict;use warnings;

use WWW::Google::Calculator;

sub calc { my $class = shift; my $sum = shift; (my $result = WWW::Google::Calculator->new()->calc($sum)) =~ s/.*= //; return $result;}

1;

Page 45: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Mock the call to Google

throws_ok( sub { Dancer::Example::Calculator::Add->get(1, 'lemons') }, qr/illegal arguments/, "catches non-numeric args");...

is(Dancer::Example::Calculator::Add->get(2, 3), 5, "Add->get() works for a simple case");

# Test that the error thrown above doesn't accidentally catch# some legit data.is(Dancer::Example::Calculator::Add->get(-4, 2), -2, "works for -ves too");is(Dancer::Example::Calculator::Add->get(0.2, 1), 1.2, "and decimals");is(Dancer::Example::Calculator::Add->get(1, -2.2), -1.2, "and -ve decimals");

Page 46: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Mock the call to Google

throws_ok( sub { Dancer::Example::Calculator::Add->get(1, 'lemons') }, qr/illegal arguments/, "catches non-numeric args");...

is(Dancer::Example::Calculator::Add->get(2, 3), 5, "Add->get() works for a simple case");

# Test that the error thrown above doesn't accidentally catch# some legit data. The bogus results prove we’re mocking.is(Dancer::Example::Calculator::Add->get(-4, 2), 7, "works for -ves too");is(Dancer::Example::Calculator::Add->get(0.2, 1), 11, "and decimals");is(Dancer::Example::Calculator::Add->get(1, -2.2), -94, "and -ve decimals");

Page 47: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Mock the call to Googleuse Class::Mock::Generic::InterfaceTester;Dancer::Example::Calculator::Add->_google_calculator( Class::Mock::Generic::InterfaceTester->new([ { method => 'calc', input => [ '2 + 3' ], output => 5 }, { method => 'calc', input => [ '-4 + 2' ], output => 7 }, { method => 'calc', input => [ '0.2 + 1' ], output => 11 }, { method => 'calc’, input => [ '1 + -2.2' ], output => -94 }, ]) );

throws_ok( sub { Dancer::Example::Calculator::Add->get(1, 'lemons') }, qr/illegal arguments/, "catches non-numeric args");...

is(Dancer::Example::Calculator::Add->get(2, 3), 5, "Add->get() works for a simple case");

# Test that the error thrown above doesn't accidentally catch# some legit data. The bogus results prove we’re mocking.is(Dancer::Example::Calculator::Add->get(-4, 2), 7, "works for -ves too");is(Dancer::Example::Calculator::Add->get(0.2, 1), 11, "and decimals");is(Dancer::Example::Calculator::Add->get(1, -2.2), -94, "and -ve decimals");

Page 48: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Mock the call to Googleuse Class::Mock::Generic::InterfaceTester;Dancer::Example::Calculator::Add->_google_calculator( Class::Mock::Generic::InterfaceTester->new([ { method => 'calc', input => [ '2 + 3' ], output => 5 }, { method => 'calc', input => [ '-4 + 2' ], output => 7 }, { method => 'calc', input => [ '0.2 + 1' ], output => 11 }, { method => 'calc’, input => [ '1 + -2.2' ], output => -94 }, ]) );

throws_ok( sub { Dancer::Example::Calculator::Add->get(1, 'lemons') }, qr/illegal arguments/, "catches non-numeric args");...

is(Dancer::Example::Calculator::Add->get(2, 3), 5, "Add->get() works for a simple case");

# Test that the error thrown above doesn't accidentally catch# some legit data. The bogus results prove we’re mocking.is(Dancer::Example::Calculator::Add->get(-4, 2), 7, "works for -ves too");is(Dancer::Example::Calculator::Add->get(0.2, 1), 11, "and decimals");is(Dancer::Example::Calculator::Add->get(1, -2.2), -94, "and -ve decimals");

$ time PERL5LIB=lib prove t/add.t t/add.t .. ok All tests successful.Files=1, Tests=8, 0 wallclock secs ...

Page 49: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Any questions?

Page 50: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Any questions?Isn’t mocking dangerous? Surely you need to test your whole application?

Page 51: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Any questions?Isn’t mocking dangerous? Surely you need to test your whole application?

Yes. You still need some end-to-end tests.

Page 52: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Any questions?Isn’t mocking dangerous? Surely you need to test your whole application?

Yes. You still need some end-to-end tests.

Mocking everything seems like an awful lot of work.

Page 53: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Any questions?Isn’t mocking dangerous? Surely you need to test your whole application?

Yes. You still need some end-to-end tests.

Mocking everything seems like an awful lot of work.

That’s not a question.

Page 54: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Any questions?Isn’t mocking dangerous? Surely you need to test your whole application?

Yes. You still need some end-to-end tests.

Mocking everything seems like an awful lot of work.

Be pragmatic about what you mock, and think aboutwhy you mock. At least make your dependencies andinterfaces mockable, even if you don’t actually doany mocking straight away.

Page 55: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Any questions?Isn’t mocking dangerous? Surely you need to test your whole application?

Yes. You still need some end-to-end tests.

Mocking everything seems like an awful lot of work.

Be pragmatic about what you mock, and think aboutwhy you mock. At least make your dependencies andinterfaces mockable, even if you don’t actually doany mocking straight away.

Have you seen the new Doctor Who?

Page 56: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Any questions?Isn’t mocking dangerous? Surely you need to test your whole application?

Yes. You still need some end-to-end tests.

Mocking everything seems like an awful lot of work.

Be pragmatic about what you mock, and think aboutwhy you mock. At least make your dependencies andinterfaces mockable, even if you don’t actually doany mocking straight away.

Have you seen the new Doctor Who?

No. Doctor Who is rubbish.

Page 57: Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd

Resources

Class::Mockable

The Art of Unit Testing, by Roy Osherove pub: Manning ISBN: 1933988274