Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 72 additions & 24 deletions t/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,45 @@
# Unit Tests for PG
# Testing PG

This directory houses the resources for testing PG. It includes a mix
of strategies for testing at different scales. It helps to catch errors
before they are found in production and prevent regressions from being
re-introduced.

The philosophy of
[Test Driven Design](https://en.wikipedia.org/wiki/Test-driven_development)
is that when a bug is found, a test is written to show it failing
and when it is fixed, the test will pass.
The unit tests are easy to run and amenable to automation. Some services
can be "mocked" so that behaviour can be tested in their absence.
All of this is to provide confidence that the code does what is intended
and a working test can be better than documentation because it shows how
the code currently works in practice.

Old references can be found on the WebWork wiki page
[Unit Testing](https://webwork.maa.org/wiki/Unit_Testing)


# Unit Tests

Comment on lines +20 to +23
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am seeing a couple of markdown lint errors here. First, no multiple blank lines, and second, multiple top level headings in the same file.

[Unit tests](https://en.wikipedia.org/wiki/Unit_testing) look at small chunks
of self-coherent code to verify the behaviour of a subroutine or module.
This is the test you write to catch corner cases or to explore code branches.
In this repository, all files with the `.t` extension are unit tests which
are found by Perl's [prove](https://perldoc.perl.org/prove) command.

The individual unit tests are located in each of the directories.
Best practice is to create a directory for each module being tested and
group similar tests together in separate files with a descriptive name,
such as **t/units/** for testing the **Units.pm** module.

Formal unit tests are located in the the `macros` and `contexts` directories that are designed to test the pg macros and contexts respectively.
Formal unit tests are located in the the `macros` and `contexts` directories
that are designed to test the pg macros and contexts respectively.

## Running the tests

```bash
cd $PG_ROOT
prove -r .
prove -lr t/
```

will run all of the tests in `.t` files within subdirectories of `t`.
Expand All @@ -23,32 +54,17 @@ prove -v pgaux.t
```

which will be verbose (`-v`).
Or you could use `prove -lv t/macros/pgaux.t` from the root directory.

## Writing a Unit Test

To write a unit test, the following is needed at the top of the file:

```perl
use warnings;
use strict;

package main;

use Test::More;
use Test::Exception;

## the following needs to include at the top of any testing down to TOP_MATERIAL

BEGIN {
die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
$main::pg_dir = $ENV{PG_ROOT};
}

use lib "$main::pg_dir/lib";
use Test2::V0;

require("$main::pg_dir/t/build_PG_envir.pl");

## END OF TOP_MATERIAL
use lib 't/lib';
use Test::PG;
```

and ensure that `PG_ROOT` is in your environmental variables.
Expand All @@ -66,7 +82,39 @@ my $f = Compute("x^2");

# evaluate f at x=2

is(check_score($f->eval(x=>2),"4"),1,"math objects: eval x^2 at x=2");
is check_score($f->eval(x=>2), '4'), 1, 'math objects: eval x^2 at x=2';
```

The `check_score` subroutine evaluates and compares a MathObject with a string representation of the answer. If the score is 1, then the two are equal.
The `check_score` subroutine evaluates and compares a MathObject with a string representation of the answer.
If the score is 1, then the two are equal.


# Integration tests

Comment on lines +90 to +93
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same errors here as before.

[Integration testing](https://en.wikipedia.org/wiki/Integration_testing)
tests components working together as a group. The files with the `.pg`
extension are used to demonstrate the output of the rendering engine.

**TODO:** add an explanation of how to run these integration tests
and their requirements.


# Test Dependencies
Comment on lines +100 to +102
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And again.


The tests for **Units.pm** have brought in a new module dependency,
[Test2](https://metacpan.org/pod/Test2::V0) which is the state of the art in
testing Perl modules. It can compare data structures, examine warnings and
catch fatal errors thrown under expected conditions. It provides many tools
for testing and randomly executes its subtests to avoid the programmer
depending on stateful data.

To make these easier to install with
[cpanm](https://metacpan.org/dist/App-cpanminus/view/bin/cpanm), there is a
[cpanfile](https://metacpan.org/dist/Module-CPANfile/view/lib/cpanfile.pod)
in the root directory. Use

cpanm --installdeps .

which will install the runtime and test dependencies.
To use the cpanfile for a minimal install skipping the test requirements,
use the `--notest` option with cpanm.
28 changes: 10 additions & 18 deletions t/contexts/fraction.t
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
use warnings;
use strict;
use Test2::V0;

package main;
# should I "use" Parser Value Parser::Legacy here instead?
Copy link
Member

@drgrice1 drgrice1 Aug 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this comment. It is certainly not in the correct place in the code, and it not clear what this comment means.


use Test::More;
use Test::Exception;
use lib 't/lib';
use Test::PG;

# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
=head2 Fraction context

BEGIN {
die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
$main::pg_dir = $ENV{PG_ROOT};
}

use lib "$main::pg_dir/lib";

require("$main::pg_dir/t/build_PG_envir.pl");
To test the reduction of fractions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reword this as something like

Fraction context tests

I know the only test in here was labeled as testing reduce fractions. However, not only is that not what it is testing (it is testing that fractions with different representations are equal -- take note @pstaabp), but in the future this will have more tests that test things with fractions other than just reduction (maybe actually one of those too!).

Also, the first header in the POD should be head1.


## END OF TOP_MATERIAL
=cut

loadMacros("PGstandard.pl", "MathObjects.pl", "contextFraction.pl");

Expand All @@ -39,9 +31,9 @@ Context("Fraction");
# require("Parser::Legacy::LimitedNumeric::Number");
# require("Parser::Legacy");

my $a1 = Compute("1/2");
my $a2 = Compute("2/4");
ok my $a1 = Compute("1/2");
ok my $a2 = Compute("2/4");

Comment on lines +34 to 36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either give these tests a name, or drop the ok test. I am not sure that these are good tests to have though.

Please name all tests. It can be very irritating to find failed tests when they do not have a name.

is($a1->value, $a2->value, "contextFraction: reduce fractions");
is $a1->value, $a2->value, 'contextFraction: reduce fractions';

done_testing();
23 changes: 7 additions & 16 deletions t/contexts/integer.t
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
use warnings;
use strict;
use Test2::V0;

package main;
# should I "use" Parser Value Parser::Legacy here instead?

use Test::More;
use Test::Exception;
use lib 't/lib';
use Test::PG;

# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
=head2 Integer context

BEGIN {
die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
$main::pg_dir = $ENV{PG_ROOT};
}

use lib "$main::pg_dir/lib";
To test for greatest common denomenators and such like.

require("$main::pg_dir/t/build_PG_envir.pl");

## END OF TOP_MATERIAL
=cut

loadMacros("MathObjects.pl", "contextInteger.pl");

Expand All @@ -33,4 +25,3 @@ ANS($b->cmp);
ok(1, "integer test: dummy test");

done_testing();

96 changes: 44 additions & 52 deletions t/contexts/toltype_digits.t
Original file line number Diff line number Diff line change
@@ -1,64 +1,56 @@
use warnings;
use strict;
use Test2::V0;

package main;
use lib 't/lib';
use Test::PG;

use Test::More;
use Test::Exception;
=head2 TolType context

# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
To test for tolerances
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least remove "To" from the beginning of this. Make it say just "Test tolerances". Either make it a proper statement or make it a complete sentence. "To test for tolerances" do what?


BEGIN {
die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
$main::pg_dir = $ENV{PG_ROOT};
}

use lib "$main::pg_dir/lib";

require("$main::pg_dir/t/build_PG_envir.pl");

## END OF TOP_MATERIAL
=cut

loadMacros("PGstandard.pl", "MathObjects.pl");

my $ctx = Context("Numeric");
$ctx->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 1);

my $pi = Real("pi");

is(check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14");
is(check_score($pi, Compute("3.141")), 1, "toltype digits: pi is 3.141");
is(check_score($pi, Compute("3.142")), 1, "toltype digits: pi is 3.142");
is(check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143");
is(check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15");

note("");
note("change tolTrunction to 0");

$ctx->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 0);
is(check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14");
is(check_score($pi, Compute("3.141")), 0, "toltype digits: pi is not 3.141");
is(check_score($pi, Compute("3.142")), 1, "toltype digits: pi is not 3.142");
is(check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143");
is(check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15");

note("");
note("set tolExtraDigits to 2");

$ctx->flags->set(
tolType => 'digits',
tolerance => 3,
tolTruncation => 0,
tolExtraDigits => 2
);
is(check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14");
is(check_score($pi, Compute("3.141")), 0, "toltype digits: pi is not 3.141");
is(check_score($pi, Compute("3.142")), 1, "toltype digits: pi is not 3.142");
is(check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143");
is(check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15");

is(check_score($pi, Compute("3.1416")), 1, "toltype digits: pi is 3.1416");
is(check_score($pi, Compute("3.1415888")), 1, "toltype digits: pi is 3.1415888");
is(check_score($pi, Compute("3.1415")), 0, "toltype digits: pi is not 3.1415");
subtest 'set tolTrunction to 1' => sub {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tolTrunction is misspelled throughout this file in the added test names. Please fix.

$ctx->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 1);

is check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14";
is check_score($pi, Compute("3.141")), 1, "toltype digits: pi is 3.141";
is check_score($pi, Compute("3.142")), 1, "toltype digits: pi is 3.142";
is check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143";
is check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15";
};

subtest 'set tolTrunction to 0' => sub {
$ctx->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 0);

is check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14";
is check_score($pi, Compute("3.141")), 0, "toltype digits: pi is not 3.141";
is check_score($pi, Compute("3.142")), 1, "toltype digits: pi is not 3.142";
is check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143";
is check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15";
};

subtest 'set tolExtraDigits to 2' => sub {
$ctx->flags->set(
tolType => 'digits',
tolerance => 3,
tolTruncation => 0,
tolExtraDigits => 2
);

is check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14";
is check_score($pi, Compute("3.141")), 0, "toltype digits: pi is not 3.141";
is check_score($pi, Compute("3.142")), 1, "toltype digits: pi is not 3.142";
is check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143";
is check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15";

is check_score($pi, Compute("3.1416")), 1, "toltype digits: pi is 3.1416";
is check_score($pi, Compute("3.1415888")), 1, "toltype digits: pi is 3.1415888";
is check_score($pi, Compute("3.1415")), 0, "toltype digits: pi is not 3.1415";
};

done_testing();
Loading