I am trying to write tests for a class that reads input from a file and creates objects of another class from that data. I've already written tests for the latter class, but I'm stuck on how to write tests for the former.
For example, the Widget class looks something like this:
package Widget;
sub new
{
my ( $class, %params ) = @_;
# etc
}
sub get_color { ... }
This class is fairly straightforward and I wrote a series of tests for it. No problem.
I also have a Container class that holds Widget objects. It looks something like this:
package Container;
sub new { ... }
sub load_widgets
{
my ( $self, $filename ) = @_;
open( my $infh, '<', $filename ) or croak "...";
while( my $line = <$infh> )
{
# parse data
my $widget_object = Widget->new( %params );
$self->{_Container}{$id} = $widget_object;
}
}
sub get_widgets_by_color
{
my ( $self, $color ) = @_;
# etc
}
I need to write tests for the Container class, but to do that I need to create a Container object with some test data. What is the best way to achieve this? I thought of a number of ways to approach this problem:
-
Provide an input file that contains test data along with the container.t file.
This means that the tests are dependent on both the successful loading and operation of Widget.pm and the input file that contains the test data. It also means that the test data is separate from the container.t file, so it would be easy for the two to get out of sync.
-
Have the test file create an input file with test data at runtime, then delete it when the tests are complete.
This approach would keep the test data in the same file as the tests themselves, but it would be dependent on creating the file successfully. It would also be dependent on the successful loading and operation of Widget.pm and the input file that contains the test data (as above).
-
Use Test::MockObject and/or Test::MockModule to mock the Widget class.
This would eliminate the dependency on Widget.pm but it would require providing mock methods where needed and it wouldn't allow me to do any integration testing. It also wouldn't eliminate the dependency on the input file containing the test data.
-
Refactor Container.pm to separate the open statement from the code that processes the data.
This would allow me to pass a filehandle (such as \*DATA) to the routine, which would eliminate the dependency of an external test input file (the method containing the open code simply wouldn't get tested), but it seems like an unnecessary complication to Container.pm added simply for the sake of testing. Also, it would still be dependent on the successful loading and operation of Widget.pm.
-
Refactor Container.pm to separate the widget storage statement from the code that processes the data.
By creating an add_widget method, I could use load_widgets to only read the input data. I could skip testing load_widgets and simply use add_widget to create the Container object for the rest of the tests. This approach would also be dependent on the successful loading and operation of Widget.pm.
The idea of creating an add_widget method appeals to me. That, combined with mocking the Widget class, is the direction that I'm leaning towards. I'm still learning how to design classes and write tests, though, so please whack me with the clue stick.
Which one (or more) of these approaches sounds like the best design? Are there other alternatives that aren't listed here? I've never used Test::MockObject or Test::MockModule, so if that's the best way to go I'd appreciate some pointers.
Many thanks in advance.
Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
Read Where should I post X? if you're not absolutely sure you're posting in the right place.
Please read these before you post! —
Posts may use any of the Perl Monks Approved HTML tags:
- a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
| |
For: |
|
Use: |
| & | | & |
| < | | < |
| > | | > |
| [ | | [ |
| ] | | ] |
Link using PerlMonks shortcuts! What shortcuts can I use for linking?
See Writeup Formatting Tips and other pages linked from there for more info.