SlideShare una empresa de Scribd logo
1 de 49
Descargar para leer sin conexión
josh mcadams
doubleclick/performics
stonehenge.com
introduction




what is testing?
introduction


Testing seems like a very simple concept.

Testing is the process of doing something, collecting the results of
what you just did, and then verifying that the results are what you
expected.

Even simpler, software testing can be reduced to saying that given a
known input, an expected output is produced.

Even with a simple definition, testing is a very complex subject that
is continually argued about.
introduction


Unit tests are tests, so they take the result of some action and
validate that result against what was expected.

Unit tests check small components of your system to make sure
that they are functioning correctly.

An automated test is a test that doesn’t necessarily require any
human intervention in order to run.

In Perl, you get a nice automated unit testing system for free with
prove, Test::Harness, Test::More, ExtUtils::MakeMaker,
Module::Build, and friends.
example problem




Throughout the talk, we’ll be creating a Perl module that will
calculate percentage of ownership for an owner in a condo.

The percentages are used for important things like association
elections and assessments, so they have to be correct or lawyers
will get involved.
example problem



The formula works by determining the percentage of the building
that an individual owns in proportion to the total livable space.
Common areas are not included.

The trick is that the actual square footage of any unit is weighted
using multiplier. Each floor up adds an extra hundredth to the
weight. For instance, a 600 square foot unit on the 11th floor would
be weighted at 600 * 1.11 = 666.
example problem



Don’t worry about units that span multiple floors.

Ninety-nine floors are the most that any building can have.

Don’t worry about matching up owners who have multiple units.

Underground floors all get a weight of zero, no matter how many
levels down they actually are.
working example




1   use warnings;
2   use strict;
3   use Test::More tests => 1;
4
5   BEGIN { use_ok('PercentOwnership'); }
working example


--(0)> prove t/percent_ownership.t
t/percent_ownership....
#   Failed test 'use PercentOwnership;'
#   at t/percent_ownership.t line 5.
#     Tried to use 'PercentOwnership'.
#     Error: Can't locate PercentOwnership.pm in @INC (@INC contains: ...) at (eval 3)
line 2.
# BEGIN failed--compilation aborted at t/percent_ownership.t line 5.
# Looks like you failed 1 test of 1.
t/percent_ownership....dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
        Failed 1/1 tests, 0.00% okay
Failed Test           Stat Wstat Total Fail Failed List of Failed
-------------------------------------------------------------------------------
t/percent_ownership.t    1   256     1    1 100.00% 1
Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
working example




1   package PercentOwnership;
2
3   use warnings;
4   use strict;
5
6   1;
working example




--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....ok
All tests successful.
Files=1, Tests=1, 0 wallclock secs ( 0.04 cusr +   0.02 csys =   0.06 CPU)
working example



1   use warnings;
2   use strict;
3   use Test::More tests => 2;
4
5   BEGIN { use_ok('PercentOwnership') }
6
7   can_ok( 'PercentOwnership', 'new' );
working example



--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....
#   Failed test 'PercentOwnership->can('new')'
#   at t/percent_ownership.t line 7.
#     PercentOwnership->can('new') failed
# Looks like you failed 1 test of 2.
t/percent_ownership....dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 2
        Failed 1/2 tests, 50.00% okay
Failed Test           Stat Wstat Total Fail Failed List of Failed
--------------------------------------------------------------------------
t/percent_ownership.t    1   256      2   1 50.00% 2
Failed 1/1 test scripts, 0.00% okay. 1/2 subtests failed, 50.00% okay.
working example


1   package PercentOwnership;
2
3   use warnings;
4   use strict;
5
6   sub new {}
7
8   1;
working example




--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....ok
All tests successful.
Files=1, Tests=2, 0 wallclock secs ( 0.04 cusr +   0.02 csys =   0.06 CPU)
working example



3   use Test::More tests => 3;
4
5   BEGIN { use_ok('PercentOwnership') }
6
7   can_ok( 'PercentOwnership', 'new' );
8   my $po = PercentOwnership->new();
9   isa_ok( $po, 'PercentOwnership' );
working example


--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....
#   Failed test 'The object isa PercentOwnership'
#   at t/percent_ownership.t line 9.
#     The object isn't defined
# Looks like you failed 1 test of 3.
t/percent_ownership....dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 3
        Failed 1/3 tests, 66.67% okay
Failed Test           Stat Wstat Total Fail Failed List of Failed
-----------------------------------------------------------------------
t/percent_ownership.t    1   256     3    1 33.33% 3
Failed 1/1 test scripts, 0.00% okay. 1/3 subtests failed, 66.67% okay.
working example

1 package PercentOwnership;
2
3 use warnings;
4 use strict;
5
6 sub new {
7     my ($class) = @_;
8     my $self = bless {}, $class;
9     return $self;
10 }
11
12 1;
working example




--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....ok
All tests successful.
Files=1, Tests=3, 0 wallclock secs ( 0.04 cusr +   0.02 csys =   0.06 CPU)
working example


3 use Test::More tests => 4;
4
5 BEGIN { use_ok('PercentOwnership') };
6
7 can_ok( 'PercentOwnership', 'new' );
8 my $po = PercentOwnership->new();
9 isa_ok($po, 'PercentOwnership');
10
11 can_ok( $po, 'add_unit' );
working example



6 sub new {
7     my ($class) = @_;
8     my $self = bless {}, $class;
9     return $self;
10 }
11
12 sub add_unit {}
working example




11 can_ok( $po, qw(add_unit percent_ownership) );
12
13 $po->add_unit(
14     unit_number    => 101,
15     square_footage => 450,
16     floor          => 1,
17 );
working example


6 sub new {
7     my ($class) = @_;
8     my $self = bless {}, $class;
9     return $self;
10 }
11
12 sub add_unit {}
13
14 sub percent_ownership {}
working example



11   can_ok( $po, qw(add_unit percent_ownership) );
12
13   $po->add_unit(
14       unit_number    => 101,
15       square_footage => 450,
16       floor          => 1,
17   );
18
19   is( $po->percent_ownership( unit_number => 101 )
          , 100, 'single unit condo' );
working example


--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....
#   Failed test 'single unit condo'
#   at t/percent_ownership.t line 19.
#           got: undef
#     expected: '100'
# Looks like you planned 4 tests but ran 1 extra.
# Looks like you failed 1 test of 5 run.
t/percent_ownership....dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 5
        Failed 1/4 tests, 75.00% okay
Failed Test            Stat Wstat Total Fail Failed List of Failed
-----------------------------------------------------------------------
t/percent_ownership.t     1   256     4    2 50.00% 5
Failed 1/1 test scripts, 0.00% okay. 0/4 subtests failed, 100.00% okay.
working example
1 package PercentOwnership;
2
3 use warnings;
4 use strict;
5
6 sub new {
7     my ($class) = @_;
8     my $self = bless {}, $class;
9     return $self;
10 }
11
12 sub add_unit { }
13
14 sub percent_ownership { return 100; }
15
16 1;
working example




--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....ok
All tests successful.
Files=1, Tests=5, 0 wallclock secs ( 0.04 cusr +   0.02 csys =   0.06 CPU)
working example

9 SINGLE_UNIT: {
10     my $po = PercentOwnership->new();
11     isa_ok( $po, 'PercentOwnership' );
12
13     can_ok( $po, qw(add_unit percent_ownership) );
14
15     $po->add_unit(
16         unit_number    => 101,
17         square_footage => 450,
18         floor          => 1,
19     );
20
21     is( $po->percent_ownership(
            unit_number => 101 ), 100,
22         'single unit condo' );
23 }
working example
25 TWO_UNITS: {
26     my $po = PercentOwnership->new();
27     isa_ok( $po, 'PercentOwnership' );
28
29     can_ok( $po, qw(add_unit percent_ownership) );
30
31     $po->add_unit(
32         unit_number    => 101,
33         square_footage => 450,
34         floor          => 1,
35     );
36
37     $po->add_unit(
38         unit_number    => 102,
39         square_footage => 450,
40         floor          => 1,
41     );
42
43     is( $po->percent_ownership( unit_number => 101 ), 50,
44         'single unit condo' );
45 }
working example


--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....NOK 8
#   Failed test 'single unit condo'
#   at t/percent_ownership.t line 43.
#           got: '100'
#     expected: '50'
# Looks like you failed 1 test of 8.
t/percent_ownership....dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 8
        Failed 1/8 tests, 87.50% okay
Failed Test            Stat Wstat Total Fail Failed List of Failed
-----------------------------------------------------------------------
t/percent_ownership.t     1   256     8    1 12.50% 8
Failed 1/1 test scripts, 0.00% okay. 1/8 subtests failed, 87.50% okay.
working example

5 use List::Util qw(sum);
...
13 sub add_unit {
14     my ( $self, %unit_info ) = @_;
15
16     $self->{unit_info}->{ $unit_info{unit_number} } = %unit_info;
17 }
18
19 sub percent_ownership {
20     my ( $self, %args ) = @_;
21
22     my $building_size = sum map {
        $self->{unit_info}->{$_}->{square_footage} }
23       keys %{ $self->{unit_info} };
24
25     my $unit_size =
26       $self->{unit_info}->{ $args{unit_number} }->{square_footage};
27
28     return sprintf( quot;%0.4fquot;, $unit_size / $building_size ) * 100;
29 }
working example




--(0)> prove -Ilib t/percent_ownership.t
t/percent_ownership....ok
All tests successful.
Files=1, Tests=8, 1 wallclock secs ( 0.06 cusr +   0.02 csys =   0.08 CPU)
tap


1..8
ok 1 - use PercentOwnership;
ok 2 - PercentOwnership->can('new')
ok 3 - The object isa PercentOwnership
ok 4 - PercentOwnership->can(...)
ok 5 - single unit condo
ok 6 - The object isa PercentOwnership
ok 7 # skip I'm lazy
not ok 8 - single unit condo # TODO I just havn't figured it out yet
#    Failed (TODO) test 'single unit condo'
#    at t/percent_ownership.t line 45.
#           got: '50'
#      expected: '51'
test::class
test::class
1 package TestPercentOwnership;
2
3 use warnings;
4 use strict;
5 use base qw(Test::Class);
6 use Test::More;
7
8 __PACKAGE__->runtests unless caller;
9
10 sub startup_test : Test( startup => 2 ) {
11     use_ok('PercentOwnership');
12     can_ok( 'PercentOwnership', 'new' );
13 }
14
15 1;
test::class

15 sub single_unit : Test(3) {
16     my ($self) = @_;
17     my $po = PercentOwnership->new();
18     isa_ok( $po, 'PercentOwnership' );
19     can_ok( 'PercentOwnership'
        , qw(add_unit percent_ownership) );
20
21     $po->add_unit(
22         unit_number    => 101,
23         square_footage => 450,
24         floor          => 1,
25     );
26
27     is( $po->percent_ownership( unit_number => 101 ), 100,
28         'single unit condo' );
29 }
test::differences



3 use Test::More qw(no_plan);
4
5 my $expected = 'This is a multiline
6 string of text
7 that is not the easiest thing to
8 display.';
9
10 my $got = $expected;
11 substr($got, 12, 1) = 'i';
12
13 is $got, $expected, 'The are who we thought they were';
test::differences

--(0)> prove scalar_is_deeply.t
is_deeply....
#   Failed test 'The are who we thought they were'
#   at is_deeply.t line 13.
#           got: 'This is a muitiline
# string of text
# that is not the easiest thing to
# display.'
#     expected: 'This is a multiline
# string of text
# that is not the easiest thing to
# display.'
# Looks like you failed 1 test of 1.
is_deeply....dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
        Failed 1/1 tests, 0.00% okay
Failed Test Stat Wstat Total Fail Failed List of Failed
-------------------------------------------------------------------------------
is_deeply.t     1   256     1    1 100.00% 1
Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
test::differences

3 use Test::More qw(no_plan);
4 use Test::Differences;
5
6 my $expected = 'This is a multiline
7 string of text
8 that is not the easiest thing to
9 display.';
10
11 my $got = $expected;
12 substr($got, 12, 1) = 'i';
13
14 eq_or_diff $got, $expected,
    'They are who we thought they were';
test::differences

--(0)> prove scalar_test_differences.t
test_differences....NOK 1
#   Failed test 'They are who we thought they were'
#   at test_differences.t line 14.
# +---+----------------------------------+----------------------------------+
# | Ln|Got                                |Expected                         |
# +---+----------------------------------+----------------------------------+
# * 1|This is a muitiline                 |This is a multiline              *
# | 2|string of text                      |string of text                   |
# | 3|that is not the easiest thing to |that is not the easiest thing to |
# | 4|display.                            |display.                         |
# +---+----------------------------------+----------------------------------+
# Looks like you failed 1 test of 1.
test_differences....dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
        Failed 1/1 tests, 0.00% okay
Failed Test         Stat Wstat Total Fail Failed List of Failed
-------------------------------------------------------------------------------
test_differences.t     1   256     1    1 100.00% 1
Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
test::differences



1   use warnings;
2   use strict;
3   use Test::More qw(no_plan);
4
5 my $expected = { name => 'Josh',
                    pets => [qw( ella ginger )] };
6 my $got = bless {%$expected}, 'Person';
7 $got->{name} = 'Heather';
8
9 is $got, $expected, 'Structures are different';
test::differences


--(0)> prove ref_is_deeply.t
ref_is_deeply....
#   Failed test 'Structures are different'
#   at ref_is_deeply.t line 9.
#           got: 'Person=HASH(0x183beb8)'
#     expected: 'HASH(0x183be40)'
# Looks like you failed 1 test of 1.
ref_is_deeply....dubious
        Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
        Failed 1/1 tests, 0.00% okay
Failed Test      Stat Wstat Total Fail Failed List of Failed
-----------------------------------------------------------------------
ref_is_deeply.t     1   256     1    1 100.00% 1
Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
test::differences


1   use   warnings;
2   use   strict;
3   use   Test::More qw(no_plan);
4   use   Test::Differences;
5
6 my $expected = { name => 'Josh',
                    pets => [qw( ella ginger )] };
7 my $got = bless {%$expected}, 'Person';
8 $got->{name} = 'Heather';
9
10 eq_or_diff $got, $expected,
        'Structures are different';
test::differences
--(0)> perl ref_test_differences.t
not ok 1 - Structures are different
#    Failed test 'Structures are different'
#    at ref_test_differences.t line 10.
# +----+----------------------+-------------------+
# | Elt|Got                    |Expected          |
# +----+----------------------+-------------------+
# *    0|bless( {              |{                 *
# *    1| name => 'Heather', | name => 'Josh', *
# |    2| pets => [            | pets => [        |
# |    3|    'ella',           |    'ella',       |
# |    4|    'ginger'          |    'ginger'      |
# |    5| ]                    | ]                |
# *    6|}, 'Person' )         |}                 *
# +----+----------------------+-------------------+
1..1
# Looks like you failed 1 test of 1.
test::deep


3   use Test::More qw(no_plan);
4   use Test::Deep;
5
6   my $obj = bless { name => 'x' }, 'Thing';
7
8 cmp_deeply( $obj,
9     all( isa('Thing')
    , noclass(
        subhashof( { name => 'x', rank => 'y' } )
    ) ) );
10 cmp_deeply( $obj,
    any( isa('Thing'), isa('OtherThing') ) );
test::mockobject


3 use Test::More qw(no_plan);
4 use Test::MockObject;
5
6 my $mock = Test::MockObject->new();
7 isa_ok( $mock, 'Test::MockObject' );
8
9 $mock->set_isa( 'DBI', 'DBIx::Class' );
10 isa_ok( $mock, 'DBIx::Class' );
11 isa_ok( $mock, 'DBI' );
thank you
yapc.org/America

Más contenido relacionado

La actualidad más candente

JavaScript Proven Practises
JavaScript Proven PractisesJavaScript Proven Practises
JavaScript Proven Practises
Robert MacLean
 
Unit Testing using PHPUnit
Unit Testing using  PHPUnitUnit Testing using  PHPUnit
Unit Testing using PHPUnit
varuntaliyan
 
Unit Testing Presentation
Unit Testing PresentationUnit Testing Presentation
Unit Testing Presentation
nicobn
 
T sql denali code Day of .Net
T sql denali code Day of .NetT sql denali code Day of .Net
T sql denali code Day of .Net
KathiK58
 

La actualidad más candente (20)

Fine-grained Processing of CVS Archives with APFEL
Fine-grained Processing of CVS Archives with APFELFine-grained Processing of CVS Archives with APFEL
Fine-grained Processing of CVS Archives with APFEL
 
PhpUnit Best Practices
PhpUnit Best PracticesPhpUnit Best Practices
PhpUnit Best Practices
 
Programming JVM Bytecode with Jitescript
Programming JVM Bytecode with JitescriptProgramming JVM Bytecode with Jitescript
Programming JVM Bytecode with Jitescript
 
Test driven node.js
Test driven node.jsTest driven node.js
Test driven node.js
 
FrontDays #3. Иван Федяев, Эволюция JavaScript. Обзор нововведений ECMAScript 6
FrontDays #3. Иван Федяев, Эволюция JavaScript. Обзор нововведений ECMAScript 6FrontDays #3. Иван Федяев, Эволюция JavaScript. Обзор нововведений ECMAScript 6
FrontDays #3. Иван Федяев, Эволюция JavaScript. Обзор нововведений ECMAScript 6
 
Student management system
Student management systemStudent management system
Student management system
 
JavaScript Proven Practises
JavaScript Proven PractisesJavaScript Proven Practises
JavaScript Proven Practises
 
Programming JVM Bytecode
Programming JVM BytecodeProgramming JVM Bytecode
Programming JVM Bytecode
 
Unit Testing using PHPUnit
Unit Testing using  PHPUnitUnit Testing using  PHPUnit
Unit Testing using PHPUnit
 
PHPUnit best practices presentation
PHPUnit best practices presentationPHPUnit best practices presentation
PHPUnit best practices presentation
 
Unit Testing Presentation
Unit Testing PresentationUnit Testing Presentation
Unit Testing Presentation
 
Introduction to Unit Testing with PHPUnit
Introduction to Unit Testing with PHPUnitIntroduction to Unit Testing with PHPUnit
Introduction to Unit Testing with PHPUnit
 
Phpunit testing
Phpunit testingPhpunit testing
Phpunit testing
 
T sql denali code Day of .Net
T sql denali code Day of .NetT sql denali code Day of .Net
T sql denali code Day of .Net
 
Practical JavaScript Programming - Session 6/8
Practical JavaScript Programming - Session 6/8Practical JavaScript Programming - Session 6/8
Practical JavaScript Programming - Session 6/8
 
Unit Testing in Angular(7/8/9) Using Jasmine and Karma Part-2
Unit Testing in Angular(7/8/9) Using Jasmine and Karma Part-2Unit Testing in Angular(7/8/9) Using Jasmine and Karma Part-2
Unit Testing in Angular(7/8/9) Using Jasmine and Karma Part-2
 
Test your code like a pro - PHPUnit in practice
Test your code like a pro - PHPUnit in practiceTest your code like a pro - PHPUnit in practice
Test your code like a pro - PHPUnit in practice
 
100% Code Coverage - TDD mit Java EE
100% Code Coverage - TDD mit Java EE100% Code Coverage - TDD mit Java EE
100% Code Coverage - TDD mit Java EE
 
Unit testing PHP apps with PHPUnit
Unit testing PHP apps with PHPUnitUnit testing PHP apps with PHPUnit
Unit testing PHP apps with PHPUnit
 
PHPUnit testing to Zend_Test
PHPUnit testing to Zend_TestPHPUnit testing to Zend_Test
PHPUnit testing to Zend_Test
 

Similar a Introduction To Testing With Perl

Test in action week 4
Test in action   week 4Test in action   week 4
Test in action week 4
Yi-Huan Chan
 
(C++) Change the following program so that it uses a dynamic array i.pdf
(C++) Change the following program so that it uses a dynamic array i.pdf(C++) Change the following program so that it uses a dynamic array i.pdf
(C++) Change the following program so that it uses a dynamic array i.pdf
f3apparelsonline
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
mfrost503
 

Similar a Introduction To Testing With Perl (20)

Testing Code and Assuring Quality
Testing Code and Assuring QualityTesting Code and Assuring Quality
Testing Code and Assuring Quality
 
How to write clean tests
How to write clean testsHow to write clean tests
How to write clean tests
 
Test in action week 4
Test in action   week 4Test in action   week 4
Test in action week 4
 
Php tests tips
Php tests tipsPhp tests tips
Php tests tips
 
PHPunit and you
PHPunit and youPHPunit and you
PHPunit and you
 
PL/SQL Unit Testing Can Be Fun!
PL/SQL Unit Testing Can Be Fun!PL/SQL Unit Testing Can Be Fun!
PL/SQL Unit Testing Can Be Fun!
 
Effective Unit Test Style Guide
Effective Unit Test Style GuideEffective Unit Test Style Guide
Effective Unit Test Style Guide
 
Testing in Laravel
Testing in LaravelTesting in Laravel
Testing in Laravel
 
Hidden Gems of Ruby 1.9
Hidden Gems of Ruby 1.9Hidden Gems of Ruby 1.9
Hidden Gems of Ruby 1.9
 
Test driven development_for_php
Test driven development_for_phpTest driven development_for_php
Test driven development_for_php
 
(C++) Change the following program so that it uses a dynamic array i.pdf
(C++) Change the following program so that it uses a dynamic array i.pdf(C++) Change the following program so that it uses a dynamic array i.pdf
(C++) Change the following program so that it uses a dynamic array i.pdf
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
PL/SQL Unit Testing Can Be Fun
PL/SQL Unit Testing Can Be FunPL/SQL Unit Testing Can Be Fun
PL/SQL Unit Testing Can Be Fun
 
New Features Of Test Unit 2.x
New Features Of Test Unit 2.xNew Features Of Test Unit 2.x
New Features Of Test Unit 2.x
 
Deixe o teste infectar você
Deixe o teste infectar vocêDeixe o teste infectar você
Deixe o teste infectar você
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
 
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnitMocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
 

Más de joshua.mcadams

Open Flash Chart And Perl
Open Flash Chart And PerlOpen Flash Chart And Perl
Open Flash Chart And Perl
joshua.mcadams
 

Más de joshua.mcadams (9)

Open Flash Chart And Perl
Open Flash Chart And PerlOpen Flash Chart And Perl
Open Flash Chart And Perl
 
Thank A Cpan Contributor Today
Thank A Cpan Contributor TodayThank A Cpan Contributor Today
Thank A Cpan Contributor Today
 
Utility Modules That You Should Know About
Utility Modules That You Should Know AboutUtility Modules That You Should Know About
Utility Modules That You Should Know About
 
YAPC::NA 2007 - Epic Perl Coding
YAPC::NA 2007 - Epic Perl CodingYAPC::NA 2007 - Epic Perl Coding
YAPC::NA 2007 - Epic Perl Coding
 
YAPC::NA 2007 - Customizing And Extending Perl Critic
YAPC::NA 2007 - Customizing And Extending Perl CriticYAPC::NA 2007 - Customizing And Extending Perl Critic
YAPC::NA 2007 - Customizing And Extending Perl Critic
 
YAPC::NA 2007 - An Introduction To Perl Critic
YAPC::NA 2007 - An Introduction To Perl CriticYAPC::NA 2007 - An Introduction To Perl Critic
YAPC::NA 2007 - An Introduction To Perl Critic
 
Extending Perl Critic
Extending Perl CriticExtending Perl Critic
Extending Perl Critic
 
An Introduction To Perl Critic
An Introduction To Perl CriticAn Introduction To Perl Critic
An Introduction To Perl Critic
 
Lightning Talk: An Introduction To Scrum
Lightning Talk: An Introduction To ScrumLightning Talk: An Introduction To Scrum
Lightning Talk: An Introduction To Scrum
 

Último

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Último (20)

Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source Milvus
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelNavi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 

Introduction To Testing With Perl

  • 4. introduction Testing seems like a very simple concept. Testing is the process of doing something, collecting the results of what you just did, and then verifying that the results are what you expected. Even simpler, software testing can be reduced to saying that given a known input, an expected output is produced. Even with a simple definition, testing is a very complex subject that is continually argued about.
  • 5. introduction Unit tests are tests, so they take the result of some action and validate that result against what was expected. Unit tests check small components of your system to make sure that they are functioning correctly. An automated test is a test that doesn’t necessarily require any human intervention in order to run. In Perl, you get a nice automated unit testing system for free with prove, Test::Harness, Test::More, ExtUtils::MakeMaker, Module::Build, and friends.
  • 6. example problem Throughout the talk, we’ll be creating a Perl module that will calculate percentage of ownership for an owner in a condo. The percentages are used for important things like association elections and assessments, so they have to be correct or lawyers will get involved.
  • 7. example problem The formula works by determining the percentage of the building that an individual owns in proportion to the total livable space. Common areas are not included. The trick is that the actual square footage of any unit is weighted using multiplier. Each floor up adds an extra hundredth to the weight. For instance, a 600 square foot unit on the 11th floor would be weighted at 600 * 1.11 = 666.
  • 8. example problem Don’t worry about units that span multiple floors. Ninety-nine floors are the most that any building can have. Don’t worry about matching up owners who have multiple units. Underground floors all get a weight of zero, no matter how many levels down they actually are.
  • 9. working example 1 use warnings; 2 use strict; 3 use Test::More tests => 1; 4 5 BEGIN { use_ok('PercentOwnership'); }
  • 10. working example --(0)> prove t/percent_ownership.t t/percent_ownership.... # Failed test 'use PercentOwnership;' # at t/percent_ownership.t line 5. # Tried to use 'PercentOwnership'. # Error: Can't locate PercentOwnership.pm in @INC (@INC contains: ...) at (eval 3) line 2. # BEGIN failed--compilation aborted at t/percent_ownership.t line 5. # Looks like you failed 1 test of 1. t/percent_ownership....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 1 Failed 1/1 tests, 0.00% okay Failed Test Stat Wstat Total Fail Failed List of Failed ------------------------------------------------------------------------------- t/percent_ownership.t 1 256 1 1 100.00% 1 Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
  • 11. working example 1 package PercentOwnership; 2 3 use warnings; 4 use strict; 5 6 1;
  • 12. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership....ok All tests successful. Files=1, Tests=1, 0 wallclock secs ( 0.04 cusr + 0.02 csys = 0.06 CPU)
  • 13. working example 1 use warnings; 2 use strict; 3 use Test::More tests => 2; 4 5 BEGIN { use_ok('PercentOwnership') } 6 7 can_ok( 'PercentOwnership', 'new' );
  • 14. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership.... # Failed test 'PercentOwnership->can('new')' # at t/percent_ownership.t line 7. # PercentOwnership->can('new') failed # Looks like you failed 1 test of 2. t/percent_ownership....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 2 Failed 1/2 tests, 50.00% okay Failed Test Stat Wstat Total Fail Failed List of Failed -------------------------------------------------------------------------- t/percent_ownership.t 1 256 2 1 50.00% 2 Failed 1/1 test scripts, 0.00% okay. 1/2 subtests failed, 50.00% okay.
  • 15. working example 1 package PercentOwnership; 2 3 use warnings; 4 use strict; 5 6 sub new {} 7 8 1;
  • 16. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership....ok All tests successful. Files=1, Tests=2, 0 wallclock secs ( 0.04 cusr + 0.02 csys = 0.06 CPU)
  • 17. working example 3 use Test::More tests => 3; 4 5 BEGIN { use_ok('PercentOwnership') } 6 7 can_ok( 'PercentOwnership', 'new' ); 8 my $po = PercentOwnership->new(); 9 isa_ok( $po, 'PercentOwnership' );
  • 18. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership.... # Failed test 'The object isa PercentOwnership' # at t/percent_ownership.t line 9. # The object isn't defined # Looks like you failed 1 test of 3. t/percent_ownership....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 3 Failed 1/3 tests, 66.67% okay Failed Test Stat Wstat Total Fail Failed List of Failed ----------------------------------------------------------------------- t/percent_ownership.t 1 256 3 1 33.33% 3 Failed 1/1 test scripts, 0.00% okay. 1/3 subtests failed, 66.67% okay.
  • 19. working example 1 package PercentOwnership; 2 3 use warnings; 4 use strict; 5 6 sub new { 7 my ($class) = @_; 8 my $self = bless {}, $class; 9 return $self; 10 } 11 12 1;
  • 20. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership....ok All tests successful. Files=1, Tests=3, 0 wallclock secs ( 0.04 cusr + 0.02 csys = 0.06 CPU)
  • 21. working example 3 use Test::More tests => 4; 4 5 BEGIN { use_ok('PercentOwnership') }; 6 7 can_ok( 'PercentOwnership', 'new' ); 8 my $po = PercentOwnership->new(); 9 isa_ok($po, 'PercentOwnership'); 10 11 can_ok( $po, 'add_unit' );
  • 22. working example 6 sub new { 7 my ($class) = @_; 8 my $self = bless {}, $class; 9 return $self; 10 } 11 12 sub add_unit {}
  • 23. working example 11 can_ok( $po, qw(add_unit percent_ownership) ); 12 13 $po->add_unit( 14 unit_number => 101, 15 square_footage => 450, 16 floor => 1, 17 );
  • 24. working example 6 sub new { 7 my ($class) = @_; 8 my $self = bless {}, $class; 9 return $self; 10 } 11 12 sub add_unit {} 13 14 sub percent_ownership {}
  • 25. working example 11 can_ok( $po, qw(add_unit percent_ownership) ); 12 13 $po->add_unit( 14 unit_number => 101, 15 square_footage => 450, 16 floor => 1, 17 ); 18 19 is( $po->percent_ownership( unit_number => 101 ) , 100, 'single unit condo' );
  • 26. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership.... # Failed test 'single unit condo' # at t/percent_ownership.t line 19. # got: undef # expected: '100' # Looks like you planned 4 tests but ran 1 extra. # Looks like you failed 1 test of 5 run. t/percent_ownership....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 5 Failed 1/4 tests, 75.00% okay Failed Test Stat Wstat Total Fail Failed List of Failed ----------------------------------------------------------------------- t/percent_ownership.t 1 256 4 2 50.00% 5 Failed 1/1 test scripts, 0.00% okay. 0/4 subtests failed, 100.00% okay.
  • 27. working example 1 package PercentOwnership; 2 3 use warnings; 4 use strict; 5 6 sub new { 7 my ($class) = @_; 8 my $self = bless {}, $class; 9 return $self; 10 } 11 12 sub add_unit { } 13 14 sub percent_ownership { return 100; } 15 16 1;
  • 28. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership....ok All tests successful. Files=1, Tests=5, 0 wallclock secs ( 0.04 cusr + 0.02 csys = 0.06 CPU)
  • 29. working example 9 SINGLE_UNIT: { 10 my $po = PercentOwnership->new(); 11 isa_ok( $po, 'PercentOwnership' ); 12 13 can_ok( $po, qw(add_unit percent_ownership) ); 14 15 $po->add_unit( 16 unit_number => 101, 17 square_footage => 450, 18 floor => 1, 19 ); 20 21 is( $po->percent_ownership( unit_number => 101 ), 100, 22 'single unit condo' ); 23 }
  • 30. working example 25 TWO_UNITS: { 26 my $po = PercentOwnership->new(); 27 isa_ok( $po, 'PercentOwnership' ); 28 29 can_ok( $po, qw(add_unit percent_ownership) ); 30 31 $po->add_unit( 32 unit_number => 101, 33 square_footage => 450, 34 floor => 1, 35 ); 36 37 $po->add_unit( 38 unit_number => 102, 39 square_footage => 450, 40 floor => 1, 41 ); 42 43 is( $po->percent_ownership( unit_number => 101 ), 50, 44 'single unit condo' ); 45 }
  • 31. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership....NOK 8 # Failed test 'single unit condo' # at t/percent_ownership.t line 43. # got: '100' # expected: '50' # Looks like you failed 1 test of 8. t/percent_ownership....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 8 Failed 1/8 tests, 87.50% okay Failed Test Stat Wstat Total Fail Failed List of Failed ----------------------------------------------------------------------- t/percent_ownership.t 1 256 8 1 12.50% 8 Failed 1/1 test scripts, 0.00% okay. 1/8 subtests failed, 87.50% okay.
  • 32. working example 5 use List::Util qw(sum); ... 13 sub add_unit { 14 my ( $self, %unit_info ) = @_; 15 16 $self->{unit_info}->{ $unit_info{unit_number} } = %unit_info; 17 } 18 19 sub percent_ownership { 20 my ( $self, %args ) = @_; 21 22 my $building_size = sum map { $self->{unit_info}->{$_}->{square_footage} } 23 keys %{ $self->{unit_info} }; 24 25 my $unit_size = 26 $self->{unit_info}->{ $args{unit_number} }->{square_footage}; 27 28 return sprintf( quot;%0.4fquot;, $unit_size / $building_size ) * 100; 29 }
  • 33. working example --(0)> prove -Ilib t/percent_ownership.t t/percent_ownership....ok All tests successful. Files=1, Tests=8, 1 wallclock secs ( 0.06 cusr + 0.02 csys = 0.08 CPU)
  • 34. tap 1..8 ok 1 - use PercentOwnership; ok 2 - PercentOwnership->can('new') ok 3 - The object isa PercentOwnership ok 4 - PercentOwnership->can(...) ok 5 - single unit condo ok 6 - The object isa PercentOwnership ok 7 # skip I'm lazy not ok 8 - single unit condo # TODO I just havn't figured it out yet # Failed (TODO) test 'single unit condo' # at t/percent_ownership.t line 45. # got: '50' # expected: '51'
  • 36. test::class 1 package TestPercentOwnership; 2 3 use warnings; 4 use strict; 5 use base qw(Test::Class); 6 use Test::More; 7 8 __PACKAGE__->runtests unless caller; 9 10 sub startup_test : Test( startup => 2 ) { 11 use_ok('PercentOwnership'); 12 can_ok( 'PercentOwnership', 'new' ); 13 } 14 15 1;
  • 37. test::class 15 sub single_unit : Test(3) { 16 my ($self) = @_; 17 my $po = PercentOwnership->new(); 18 isa_ok( $po, 'PercentOwnership' ); 19 can_ok( 'PercentOwnership' , qw(add_unit percent_ownership) ); 20 21 $po->add_unit( 22 unit_number => 101, 23 square_footage => 450, 24 floor => 1, 25 ); 26 27 is( $po->percent_ownership( unit_number => 101 ), 100, 28 'single unit condo' ); 29 }
  • 38. test::differences 3 use Test::More qw(no_plan); 4 5 my $expected = 'This is a multiline 6 string of text 7 that is not the easiest thing to 8 display.'; 9 10 my $got = $expected; 11 substr($got, 12, 1) = 'i'; 12 13 is $got, $expected, 'The are who we thought they were';
  • 39. test::differences --(0)> prove scalar_is_deeply.t is_deeply.... # Failed test 'The are who we thought they were' # at is_deeply.t line 13. # got: 'This is a muitiline # string of text # that is not the easiest thing to # display.' # expected: 'This is a multiline # string of text # that is not the easiest thing to # display.' # Looks like you failed 1 test of 1. is_deeply....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 1 Failed 1/1 tests, 0.00% okay Failed Test Stat Wstat Total Fail Failed List of Failed ------------------------------------------------------------------------------- is_deeply.t 1 256 1 1 100.00% 1 Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
  • 40. test::differences 3 use Test::More qw(no_plan); 4 use Test::Differences; 5 6 my $expected = 'This is a multiline 7 string of text 8 that is not the easiest thing to 9 display.'; 10 11 my $got = $expected; 12 substr($got, 12, 1) = 'i'; 13 14 eq_or_diff $got, $expected, 'They are who we thought they were';
  • 41. test::differences --(0)> prove scalar_test_differences.t test_differences....NOK 1 # Failed test 'They are who we thought they were' # at test_differences.t line 14. # +---+----------------------------------+----------------------------------+ # | Ln|Got |Expected | # +---+----------------------------------+----------------------------------+ # * 1|This is a muitiline |This is a multiline * # | 2|string of text |string of text | # | 3|that is not the easiest thing to |that is not the easiest thing to | # | 4|display. |display. | # +---+----------------------------------+----------------------------------+ # Looks like you failed 1 test of 1. test_differences....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 1 Failed 1/1 tests, 0.00% okay Failed Test Stat Wstat Total Fail Failed List of Failed ------------------------------------------------------------------------------- test_differences.t 1 256 1 1 100.00% 1 Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
  • 42. test::differences 1 use warnings; 2 use strict; 3 use Test::More qw(no_plan); 4 5 my $expected = { name => 'Josh', pets => [qw( ella ginger )] }; 6 my $got = bless {%$expected}, 'Person'; 7 $got->{name} = 'Heather'; 8 9 is $got, $expected, 'Structures are different';
  • 43. test::differences --(0)> prove ref_is_deeply.t ref_is_deeply.... # Failed test 'Structures are different' # at ref_is_deeply.t line 9. # got: 'Person=HASH(0x183beb8)' # expected: 'HASH(0x183be40)' # Looks like you failed 1 test of 1. ref_is_deeply....dubious Test returned status 1 (wstat 256, 0x100) DIED. FAILED test 1 Failed 1/1 tests, 0.00% okay Failed Test Stat Wstat Total Fail Failed List of Failed ----------------------------------------------------------------------- ref_is_deeply.t 1 256 1 1 100.00% 1 Failed 1/1 test scripts, 0.00% okay. 1/1 subtests failed, 0.00% okay.
  • 44. test::differences 1 use warnings; 2 use strict; 3 use Test::More qw(no_plan); 4 use Test::Differences; 5 6 my $expected = { name => 'Josh', pets => [qw( ella ginger )] }; 7 my $got = bless {%$expected}, 'Person'; 8 $got->{name} = 'Heather'; 9 10 eq_or_diff $got, $expected, 'Structures are different';
  • 45. test::differences --(0)> perl ref_test_differences.t not ok 1 - Structures are different # Failed test 'Structures are different' # at ref_test_differences.t line 10. # +----+----------------------+-------------------+ # | Elt|Got |Expected | # +----+----------------------+-------------------+ # * 0|bless( { |{ * # * 1| name => 'Heather', | name => 'Josh', * # | 2| pets => [ | pets => [ | # | 3| 'ella', | 'ella', | # | 4| 'ginger' | 'ginger' | # | 5| ] | ] | # * 6|}, 'Person' ) |} * # +----+----------------------+-------------------+ 1..1 # Looks like you failed 1 test of 1.
  • 46. test::deep 3 use Test::More qw(no_plan); 4 use Test::Deep; 5 6 my $obj = bless { name => 'x' }, 'Thing'; 7 8 cmp_deeply( $obj, 9 all( isa('Thing') , noclass( subhashof( { name => 'x', rank => 'y' } ) ) ) ); 10 cmp_deeply( $obj, any( isa('Thing'), isa('OtherThing') ) );
  • 47. test::mockobject 3 use Test::More qw(no_plan); 4 use Test::MockObject; 5 6 my $mock = Test::MockObject->new(); 7 isa_ok( $mock, 'Test::MockObject' ); 8 9 $mock->set_isa( 'DBI', 'DBIx::Class' ); 10 isa_ok( $mock, 'DBIx::Class' ); 11 isa_ok( $mock, 'DBI' );