Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get 100% test coverage in a Perl module that uses DBI?

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!

like image 401
BrianH Avatar asked Mar 18 '10 15:03

BrianH


People also ask

How much test coverage is enough?

Aim for 95% or higher coverage with unit tests for new application code. When developers unit test as they program, they improve the longevity and quality of the codebase. The time a development team invests in unit tests pays off with less time spent troubleshooting defects and analyzing problems later.

What is good unit test coverage?

While there is no standard for unit testing, one number often cited in the testing world is 80%. "Eighty percent is what I usually see as the gating standard for code coverage in corporate shops," said Tim Ottinger, a senior consultant at Industrial Logic. "Any higher or lower than that is unusual."


2 Answers

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:

  • More on 100% Test Coverage
  • 100% Test Coverage?

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.

like image 124
brian d foy Avatar answered Oct 12 '22 04:10

brian d foy


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.

like image 30
DVK Avatar answered Oct 12 '22 03:10

DVK