views:

143

answers:

2

I am a bit new to the Devel::Cover module, but have found it very useful in making sure I am not missing tests.

A problem I am running into is understanding the report from Devel::Cover. I've looked at the documentation, but can't figure out what I need to test to get 100% coverage.

Edit - I should make it clear that I am not saying I need 100% coverage, because as multiple people point out, 100% coverage is a loose term, does not mean that my code is bug free, and may not always be completely necessary. Since I am new at Devel::Cover, I am interested to know why my code is not 100% coverage, in case I am missing some important tests.

Here is the output from the cover report:

line  err   stmt   bran   cond    sub    pod   time   code
...
36                                                    sub connect_database {
37             3                    3      1   1126       my $self = shift;
38             3    100                          24       if ( !$self->{dsn} ) {
39             1                                  7           croak 'dsn not supplied - cannot connect';
40                                                        }
41    ***      2            33                   21       $self->{dbh} = DBI->connect( $self->{dsn}, q{}, q{} )
42                                                          || croak "$DBI::errstr";
43             1                                 11       return $self;
44                                                    }
...
line  err      %      l  !l&&r !l&&!r   expr
----- --- ------ ------ ------ ------   ----
41    ***     33      1      0      0   'DBI'->connect($$self{'dsn'}, '', '') || croak("$DBI::errstr")

And here is and example of my code that tests this specific line:

my $database = MyModule::Database->new( { dsn => 'Invalid DSN' });
throws_ok( sub { $database->connect_database() }, 
   qr/Can't connect to data source/, 
   'Test connection exception (invalid dsn)' );

This test passes - the connect does throw an error and fulfills my "throws_ok" test.

I do have some tests that test for a successful connection, which is why I think I have 33% coverage, but if I'm reading it correctly, cover thinks I am not testing the "|| croak" part of the statement. I thought I was, with the "throws_ok" test, but obviously I am missing something.

Does anyone have advice on how I can test my DBI->connect line successfully?

Thanks!

Edit:

brian tipped me off to the HTML report and the truth table that explains why line #41 is not passing. The only problem is that I can't figure out what it is telling me. I guess the real core of my question is why is this specific line not passing coverage.

Here is the truth table:

LINE # %  # coverage    # condition
41   # 33 # A | B | dec # 'DBI'->connect($$self{'dsn'}, '', '') || croak("$DBI::errstr")
     #    # 0 | 0 | 0   #
     #    # 0 | 1 | 1   #
     #    # 1 | X | 1   # (THIS LINE IS Green - the rest are red)

If anyone could help explain this truth table, I'd appreciate it. It has also been mentioned that to pass the coverage I need to have a mock database object, but I don't quite see how anything in the coverage results that would clue me in to this.

Thanks again!

+4  A: 

You must mock DBI library (or any other external dependency) in order to fully cover your unit tests.

You can use Test::MockObject, or any other mocking approach (e.g. our company developed our own custom very powerful Perl mocking library).

Also see this article specifically on DBI mocking.

Also, please ensure you're using the latest version of Devel::Cover. I once spent 3 days struggling with a unit test until it dawned on me that the bug was not in my code, and not in my unit test, but in the older version of Devel::Cover my company had installed. It literally ignored certain code paths in "A||B" cases.

DVK
DBD::Mock is pretty handy for this. http://search.cpan.org/perldoc?DBD::Mock
daotoad
LOL good timing daotoad - I just updated with a link mentioning DBD::Mock 1 second ago :)
DVK
So cover is specifically looking for me to mock my database, and establish a mock connection, even though I successfully connected and successfully aborted?I'm a little familiar with Test::MockObject, but didn't know about DBD::Mock. I'll have to look into those further.
BrianH
+7  A: 

Also, don't get too hung up on 100% test coverage. The goal is to fully test your application, not get perfect scores in Devel::Cover. See Ovid's posts on the subject:

In your case, it looks like you don't cover all the branches, so you don't get perfect scores. You need to tests that exercise both sides of that ||. You're getting 33% percent coverage because you only handle one-third of the cases for that line. The HTML report from Devel::Cover shows you the truth table and which cases you are missing.

The truth table shows you the possible states that you have to cover for the branching. A 1 shows you a condition which is true, 0 shows you a condition that is false, and X shows you a condition you won't reach. You have to test all combinations that can execute. Since the || is a short-circuit operator, you don't have to test conditions once one of them passes:

 0 || 1     connect to database fails and croak succeeds
 0 || 0     connect to database fails and croak fails (unlikely)
 1 || X     connect to database succeeds, so short circuit

This is a bit unrelated to your particular problem, but I find it comes up often in these problems. Although Effective Perl Programming is a month of so away from hitting the shelves, Josh McAdams spent quite a bit of time talking about dependency injection in Perl. If you're having a tough time testing your code, you usually have a design problem. If you're internally generating database objects in subroutines, for instance, you are painting yourself into a corner. That's why it might be hard to test. This may not be the problem in your case, but it's something to think about.

brian d foy
I think Ovid's main point was that 100% test coverage is not synonymous with "no bugs", as opposed to rejecting the idea of 100% coverage. "If it's not covered, it's broken. Every. Single. Time."
DVK
Indeed that was Ovid's point. That's why I said basically the same thing in my second sentence.
brian d foy
Yep - Those 2 blog posts are actually what got me looking at Devel::Cover. I'm not really trying to get 100% test coverage - I was more curious on if I was missing a test in this specific instance that I should be testing.And good advice on having hard time testing code. In this case it was a test that was already written. I wanted to see how well I passed Devel::Cover, and got stumped on the DBI->connect.Thanks for the advice!
BrianH
Thank you very much! Your truth table explanation helps me out a ton. Thanks for sharing your wisdom!
BrianH