Presentation is loading. Please wait.

Presentation is loading. Please wait.

Unit Testing You ain’t doing it, and you should David Cantrell Chief Plumber, UK2 Ltd.

Similar presentations


Presentation on theme: "Unit Testing You ain’t doing it, and you should David Cantrell Chief Plumber, UK2 Ltd."— Presentation transcript:

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

2 Unit Testing You ain’t doing it, and you should David Cantrell david@cantrell.org.ukdavid@cantrell.org.uk Chief Plumber, UK2 Ltd 12:20 ees me, ees mario! 12:21 dc is promoted to chief plumber, in charge of princess rescue and mushroom eating Boss

3 This is your code

4

5

6 Let’s run a test GET /foo/bar/baz

7 Let’s run a test GET /foo/bar/baz

8 Let’s run a test GET /foo/bar/baz

9 Let’s run a test GET /foo/bar/baz

10 Let’s run a test GET /foo/bar/baz

11 Let’s run a test GET /foo/bar/baz

12 Let’s run a test GET /foo/bar/baz

13 Let’s run a test GET /foo/bar/baz

14 Let’s run a test GET /foo/bar/baz

15 Let’s run a test GET /foo/bar/baz not ok 43572 GET /customer/94/invoice got right data

16 Where was that test failure?

17 Why do we test?

18 Testing new code did I get this new feature right?

19 Why do we test? Testing new code did I get this new feature right? Regression testing did I break any existing code?

20 Why do we test? Testing new code did I get this new feature right? Regression testing did I break any existing code? what did I break?

21 Why do we test? Testing new code did I get this new feature right? Regression testing did I break any existing code? what did I break? where?

22 The solution (sort of)

23

24

25 and so on ad infinitum et tedium

26 That solution sucks Great, you just tested most of your code multiple times.

27 That solution sucks Great, you just tested most of your code multiple times. Your tests took two hours.

28 That solution sucks Great, you just tested most of your code multiple times. Your tests took two hours. And again when you think you fixed them.

29 But that wasn’t unit testing

30 This is unit testing

31

32

33 Repeat ad infinitum et tedium

34 This is unit testing You just tested all of your code once! Your tests took twenty minutes!

35 That’s the end of the theory Any questions?

36 Let’s look at some code

37 (it doesn’t do much)

38 Let’s look at some code

39 End to end tests package 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" );

40 End to end tests package 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" ); }

41 End to end tests package 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...

42 Get rid of the front-end layer package 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 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");

43 Prepare to mock the call to Google package 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 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");

44 Prepare to mock the call to Google package 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;

45 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");

46 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");

47 Mock the call to Google use 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");

48 Mock the call to Google use 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...

49 Any questions?

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

51 Any questions? Isn’t mocking dangerous? Surely you need to test your whole application? Yes. You still need some end-to-end tests.

52 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.

53 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.

54 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 about why you mock. At least make your dependencies and interfaces mockable, even if you don’t actually do any mocking straight away.

55 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 about why you mock. At least make your dependencies and interfaces mockable, even if you don’t actually do any mocking straight away. Have you seen the new Doctor Who?

56 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 about why you mock. At least make your dependencies and interfaces mockable, even if you don’t actually do any mocking straight away. Have you seen the new Doctor Who? No. Doctor Who is rubbish.

57 Resources Class::Mockable The Art of Unit Testing, by Roy Osherove pub:Manning ISBN:1933988274


Download ppt "Unit Testing You ain’t doing it, and you should David Cantrell Chief Plumber, UK2 Ltd."

Similar presentations


Ads by Google