Imagine writing tests for your car. If you turn the wheel, do the tires turn left? What about right? If you hit the brakes, do the rear lights light up? Of course, before you can perform any of these tests, you need to open the door, sit in the driver's seat, put on the seat belt, and start the car. When you're done, you must stop the car, unbuckle, and disembark. What a pain it would be to perform each step for each individual test ”you'd have to get in and start the car three times! It would be much easier if, before each test, your car arrived fully prepared and then magically transported you to the driver's seat, buckled you in, and fastened your crash helmet securely. This is exactly what fixtures are: parts of an environment created before tests run and removed after the tests finish. This lab shows how to create fixtures for your tests using setup and teardown methods , which eliminates duplication and makes your test code more sane. How do I do that? Copy the Queue module and queue.t test file from "Writing Test Cases." However, the test module needs to change slightly. The new Queue::Test needs a new method, setup_queues( ) , to create a test fixture for the other test methods to use. Save the following code as Queue/Test.pm : package Queue::Test; use base 'Test::Class'; use Queue; use Test::More; sub setup_queues : Test( setup => 2 ) { my ($self) = @_; $self->{empty} = Queue->new( ); $self->{twoitems} = Queue->new(qw( howdy bonjour )); isa_ok( $self->{$_}, 'Queue' ) for qw( empty twoitems ); } sub size : Test(2) { my ($self) = @_; is( $self->{empty}->size( ), 0, 'an empty queue' ); is( $self->{twoitems}->size( ), 2, 'a queue with some elements' ); } sub enqueue : Test(1) { my ($self) = @_; $self->{twoitems}->enqueue($_) for qw( ciao yo ); is( $self->{twoitems}->size( ), 4, 'queue is now larger' ); } sub dequeue : Test(3) { my ($self) = @_; is( $self->{empty}->dequeue( ), undef, 'empty queue' ); is( $self->{twoitems}->dequeue( ), 'howdy', 'first item' ); is( $self->{twoitems}->dequeue( ), 'bonjour', 'second item' ); } 1; Run queue.t verbosely with prove : $ prove -v queue.t queue....# # Queue::Test->dequeue 1..12 ok 1 - The object isa Queue ok 2 - empty queue ok 3 - queue is now larger ok 4 - first item ok 5 - second item ok 6 - queue is now smaller # # Queue::Test->enqueue ok 7 - The object isa Queue ok 8 - queue is now larger # # Queue::Test->size ok 9 - The object isa Queue ok 10 - an empty queue ok 11 - The object isa Queue ok 12 - a queue with some elements ok All tests successful. Files=1, Tests=12, 0 wallclock secs ( 0.16 cusr + 0.03 csys = 0.19 CPU) What just happened ? Every test method receives a hash reference as its first argument. This is the test object, and it exists to pass data from the fixtures to the tests. Feel free to add whatever you want to it. Notice the output of prove -v ? There are a total of six isa checks, yet setup_queues( ) is the only method that calls isa_ok( ) , and it does so only twice. What happened? setup_queues( ) has the attribute Test(setup=> 2) . Note: Test(setup)is the same as Test(setup =>0). The same goes for the teardown, startup, and shutdown attributes. It never hurts to be verbose, though . The setup_queues( ) method prepares and checks the type of two Queue objects that all of the test methods use. Test::Class calls setup_queue( ) before each test method, so it runs three times in this test file. Each test method receives two fresh Queue objects in the test object. This simplifies the testing code by eliminating duplicate code, making it easier to add new tests. What about... Q: | What if I need to clean up the fixture after each test? | A: | Use a teardown method by creating a new method with the attribute Test(teardown => n ) . Teardown methods run after each test method. | Q: | Is it possible to have setup and teardown methods for the entire class? | A: | Sure! Test::Class calls these startup and shutdown methods. Declare them with the attributes Test(startup => n ) and Test(shutdown => n ), respectively. Each startup and shutdown method runs only once per test file. It receives the test object as the first argument, just like the other test methods. Because startup methods run only once at the beginning of the test, they do not have the chance to reinitialize whatever they store in the test object as setup methods do. | |