User Tools

Site Tools


dev:testing:debugging_perl_unit_tests

Using a debugger in Perl unit tests

It can be difficult to use the Perl debugger to debug Evergreen's Perl code while the system is running.  One workaround is to run a unit test that calls the Perl code you'd like to debug.  This approach allows you to provide specific known values to the subroutines in question.

The following steps work for unit tests (i.e. the tests in Open-ILS/src/perlmods/t).  I don't think they work for the live tests.  These are written using the docker dev containers.  If you use a different development environment, you'll probably have to adjust them.

If there is a test that already calls the subroutine you're interested in

  1. Note the filename of the test, and the line number that calls the Perl code you'd like to debug.  In our case, we'd like to investigate OpenILS::Application::Storage::QueryParser::remove_search_field subroutine, which is currently called on line 25 of t/21-QueryParser.t.
  2. Start a docker dev container using the instructions on docker hub
  3. docker ps # gives you a list of running containers.  Find the ID or name of the one you just started
  4. docker exec -it container_id_or_name bash
  5. su opensrf
  6. cd /home/opensrf/repos/Evergreen/Open-ILS/src/perlmods
  7. perl -Ilib -d -T t/21-QueryParser.t # -T is only necessary if the first line of the test specifies perl -T
  8. Congratulations!  You are now in the debugger!
  9. Type n then enter a few times until you reach the context of a line in t/21-QueryParser.t
  10. Add a breakpoint for the line that calls the code you want to explore (in our case, line 25): b 25
  11. Enter c to proceed to the breakpoint
  12. The context should now say: main::(t/21-QueryParser.t:25):  $QParser->remove_search_field('author', 'personal');
  13. Enter s to step into the query parser's remove_search_field method!
  14. To get more context of the surrounding code, enter l (lower case L)
  15. To move forward, enter n
  16. If you want to know the value of a particular variable or expression, enter x then the expression.  For example, you could enter x $pkg->search_fields->{'author'} to get a list of the current author search fields in the parser.
  17. If you need to make some changes to the source code, do so in a separate editor.  Then enter R to restart the test, which will now take your changes into account.  Maybe. This can be buggy in my experience.
  18. When you are done exploring: enter q

If you have failing tests

Sometimes your test code or production code will have an error that causes it to die. Sometimes this will cause multiple tests or subtests to fail. If this happens, you can run into frustrating situations where you set a breakpoint within a failing test, enter c, and instead of allowing you to debug that line, you get the message "Debugged program terminated." To avoid this:

  • Make sure to put your breakpoint before or at the first line that is failing, or
  • If you want to debug, say the second failing test, you can use Test::More's ''SKIP'' feature to skip the first test before starting your debugging session and putting a breakpoint on the second failing test.

If there is not an existing unit test for the code you want to explore

Most of Evergreen's code is unfortunately not (yet) covered by a unit test.  However, in some cases you can throw together a simple unit test for the purposes of debugging, and it has the added benefit of improving our test coverage.  This will be easier for simple subroutines that don't depend on many other parts of the code.

Testing and debugging simple subroutines and methods

OpenILS::Application::Acq::EDI::nice_string doesn't have a test, let's test it!

use warnings; use strict;
use Test::More tests => 2;
use_ok 'OpenILS::Application::Acq::EDI';
my $edi = OpenILS::Application::Acq::EDI->new;
$edi->nice_string('Hello');
pass();

And once we have finished our debugging, we can turn this into a legit test by replacing `pass()` with a real assertion.

use warnings; use strict;
use Test::More tests => 2;
use_ok 'OpenILS::Application::Acq::EDI';
my $edi = OpenILS::Application::Acq::EDI->new;
is $edi->nice_string('Hello'),
    'Hello',
    'String Hello is left intact';

Run it with prove, and then let's submit that patch to boost our code coverage!

Testing and debugging methods with dependencies

If it relies on a complex object, but you can pass it in, you can use Test::MockObject.  If it relies on a complex object but it is setup somewhere you can't control in your test (e.g. the initializer creates a new CSTORE editor), use Test::MockModule.

Further resources

* The Perl 5 Debugger by Ricardo Signes (youtube video) – a good talk on the Perl debugger

dev/testing/debugging_perl_unit_tests.txt · Last modified: 2024/12/01 11:20 by sandbergja

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki

© 2008-2022 GPLS and others. Evergreen is open source software, freely licensed under GNU GPLv2 or later.
The Evergreen Project is a U.S. 501(c)3 non-profit organization.