File Coverage

blib/lib/Test/Lite.pm
Criterion Covered Total %
statement 44 263 16.7
branch 5 98 5.1
condition 0 14 0.0
subroutine 12 41 29.2
pod 22 29 75.8
total 83 445 18.6


line stmt bran cond sub pod time code
1             package Test::Lite;
2              
3             $Test::Lite::VERSION = '0.009';
4             $Test::Lite::DieOnSyntaxError = 0;
5              
6             =head1 NAME
7              
8             Test::Lite - A small Perl Test Library
9              
10             =head1 DESCRIPTION
11              
12             Test::Lite is just that. A minimal test library based on the brilliant L. The main focus of this project
13             was to learn more about testing while building this module. A pretty overlooked subject amongst some Perl developers is testing (myself included).
14             I've tried to offer some different features in this module, but you're probably still better off with L, L or one of the many other
15             testing libraries out there.
16              
17             =head1 SYNOPSIS
18              
19             Using Test::Lite is pretty similar to other test modules (Why break tradition, eh?)
20              
21             use Test::Lite;
22              
23             my $a = { name => 'World' };
24             my $b = { name => 'Worlds' };
25            
26             diff ($a, $b, "Difference between hash 'a' and hash 'b'");
27              
28             my @non_ref qw(not a ref);
29             my $true_ref = [1, 2, 3];
30             is_ref(@non_ref, 'Name of test');
31             is_ref($true_ref => 'HASH', 'Name of test'); # Checks to see if $true_ref returns a HASH
32              
33             use_ok [qw( A::Module Another::Module )];
34              
35             =cut
36              
37 2     2   40098 use strict;
  2         5  
  2         178  
38 2     2   11 use warnings;
  2         4  
  2         57  
39              
40 2     2   53 use 5.010;
  2         10  
  2         89  
41 2     2   11 use Scalar::Util 'looks_like_number';
  2         3  
  2         254  
42 2     2   2301 use Sub::Mage ':Class';
  2         20761  
  2         17  
43             extends 'Test::Builder::Module';
44              
45             my $CLASS = __PACKAGE__;
46              
47             sub import {
48 2     2   21 my ($class, @args) = @_;
49 2         18 my $pkg = caller(1);
50 2         7 for (@args) {
51 0 0       0 if ($_ eq ':strict') {
52 0         0 warnings->import;
53 0         0 strict->import;
54             }
55             }
56 2         14 $CLASS->_export_defs(qw/
57             is
58             ok
59             has_key
60             cmp_ok
61             diff
62             diag
63             plan
64             use_ok
65             can_ok
66             isa_ok
67             is_ref
68             like
69             explain
70             extended
71             methods
72             subtest
73             todo_start
74             todo_end
75             is_passing
76             count
77             note
78             level
79             finfo
80             done_testing
81             /);
82             }
83              
84             sub _export_defs {
85 2     2   13 my ($self, @defs) = @_;
86 2         6 my $pkg = caller(1);
87 2         5 for (@defs) {
88 48         3804 exports $_ => ( into => $pkg );
89             }
90             }
91              
92 0     0 0 0 sub dieonsyntax { return $Test::Lite::DieOnSyntaxError; }
93              
94             sub ok {
95 0     0 1 0 my ($val, $name) = @_;
96 0         0 my $tb = $CLASS->builder;
97 0         0 $tb->ok(@_);
98             }
99              
100             sub cmp_ok {
101 0     0 1 0 my ($a, $type, $b, $name) = @_;
102 0         0 my $tb = $CLASS->builder;
103 0         0 $tb->cmp_ok(@_);
104             }
105              
106             sub is {
107 1     1 1 600 my ($a, $b, $args, $name) = @_;
108 1         5 my $tb = $CLASS->builder;
109 1 50       13 if (scalar keys %$args < 1) {
110 0 0       0 if (looks_like_number($b)) { $tb->is_num($a, $b, $name); }
  0         0  
111 0         0 else { $tb->is_eq($a, $b, $name); }
112             }
113             else {
114 1         3 my $type;
115             my $skip;
116 1         3 for (keys %$args) {
117 1 50       6 $type = $args->{type}
118             if $_ eq 'type';
119 1 50       85 $skip = $args->{skip}
120             if $_ eq 'skip';
121             }
122            
123 1 50       4 if ($type eq 'Int') {
    0          
124 1 50       8 if (! looks_like_number($b)) {
125 0         0 my $err = "Type for this test is set to 'Int', but a numeric character was not being tested for";
126 0 0       0 if ($skip) {
127 0         0 $tb->skip($err);
128             }
129 0         0 else { say "not ok " . $tb->current_test() . " - $err"; }
130             }
131 1         5 else { $tb->is_num($a, $b, $name); }
132             }
133             elsif ($type eq 'Str') {
134 0 0       0 if (looks_like_number($b)) {
135 0         0 my $err = "Type for this test is set to 'Str', but a numeric character entered as a value";
136 0 0       0 if ($skip) {
137 0         0 $tb->skip($err);
138             }
139             else {
140 0         0 say "not ok " . $tb->current_test() . " - $err";
141             }
142             }
143 0         0 else { $tb->is_eq($a, $b, $name); }
144             }
145             }
146             }
147              
148             sub extended {
149 0     0 1 0 my ($mother, @base) = @_;
150 0         0 my $tb = $CLASS->builder;
151 0         0 my $test = $tb->current_test();
152 0         0 my @extends;
153 2     2   2831 no strict 'refs';
  2         6  
  2         3474  
154 0         0 my $mom = $mother;
155 0         0 $mother = "$mother\::";
156 0         0 for my $child (@base) {
157 0         0 foreach my $key (keys %{$mother}) {
  0         0  
158 0 0       0 if (substr($key, -2, -1) eq ':') {
159 0         0 push @extends, substr("$mother$key", 0, -2);
160             }
161             }
162             }
163 0         0 $mother = "";
164 0         0 for $mother (@extends) {
165 0         0 DEEP_SEARCH: foreach my $key (keys %{"$mother\::"}) {
  0         0  
166 0 0       0 if (substr($key, -2, -1) eq ':') {
167 0         0 push @extends, "$mother\::" . substr("$key", 0, -2);
168             }
169 0 0       0 if (scalar keys %{$key} > 0) {
  0         0  
170 0         0 $mother = "$mother$key";
171 0         0 next DEEP_SEARCH;
172             }
173             }
174             }
175 0 0       0 if (scalar @extends > 0) {
176 0         0 for my $extend (@base) {
177 0 0       0 if (! grep { $_ eq $extend } @extends) {
  0         0  
178 0         0 $tb->ok(0, "$mom does not extend $extend");
179 0         0 return 1;
180             }
181             }
182 0         0 $tb->ok(1, "$mom extends " . join(q{, }, @base));
183 0         0 return 0;
184             }
185             else {
186 0         0 $tb->skip("No extends found in $mom, so let's move on");
187 0         0 return 0;
188             }
189             }
190              
191             sub use_ok {
192 0     0 1 0 my ($use, $imports) = @_;
193 0         0 my $tb = $CLASS->builder;
194 0         0 my $test = $tb->current_test();
195 0         0 my $pkg = caller();
196 0 0       0 if (ref($use) eq 'ARRAY') {
197 0         0 my @failed;
198             $tb->subtest( 'Use multiple modules', sub {
199 0     0   0 for (@$use) {
200 0         0 eval qq{package $pkg;
201             use $_;
202             1;
203             };
204 0         0 $tb->unlike( $@, qr/Can't locate/, "use $_");
205             }
206 0         0 });
207             }
208             else {
209 0 0       0 if (ref($imports) eq 'ARRAY') {
210 0         0 my $imps = join "\n", @$imports;
211 0         0 eval qq{package $pkg;
212             use $use qw/$imps/;
213             1;
214             };
215             }
216             else {
217 0         0 eval qq{package $pkg;
218             use $use;
219             1;
220             };
221             }
222 0 0       0 if ($@) { say "not ok $test - Could not 'use $use'"; return 1 }
  0         0  
  0         0  
223 0         0 else { $tb->ok( $use, "use $use" ); }
224             }
225             }
226              
227             sub like {
228 0     0 1 0 my ($this, $like, $name) = @_;
229 0         0 my $tb = $CLASS->builder;
230 0         0 my $test = $tb->current_test;
231 0 0       0 if (ref($like) ne 'Regexp') {
232 0         0 my $err = "Second paremeter must be a Regex";
233 0 0       0 if ($CLASS->dieonsyntax) { say "not ok $test - $err"; }
  0         0  
234 0         0 else { $tb->skip($err); }
235 0         0 return 1;
236             }
237            
238 0         0 $tb->like(@_);
239             }
240              
241             sub unlike {
242 0     0 1 0 my ($this, $unlike, $name) = @_;
243 0         0 my $tb = $CLASS->builder;
244 0         0 my $test = $tb->current_test;
245 0 0       0 if (ref($unlike) ne 'Regexp') {
246 0         0 my $err = "Second paremeter must be a Regex";
247 0 0       0 if ($CLASS->dieonsyntax) { say "not ok $test - $err"; }
  0         0  
248 0         0 else { $tb->skip($err); }
249 0         0 return 1;
250             }
251              
252 0         0 $tb->like(@_);
253             }
254              
255             sub diff {
256 0     0 1 0 my ($a, $b, $name) = @_;
257 0         0 my $tb = $CLASS->builder;
258 0         0 my $test = $tb->current_test;
259 0 0 0     0 if (! ref($a) || ! ref($b)) {
260 0         0 my $err = "diff expects ArrayRef or HashRef only";
261 0 0       0 if ($CLASS->dieonsyntax) { say "not ok $test - $err"; }
  0         0  
262 0         0 else { $tb->skip($err); }
263 0         0 return 1;
264             }
265              
266 0         0 $tb->is_eq($tb->explain($a), $tb->explain($b), $name);
267             }
268              
269             sub can_ok {
270 0     0 1 0 my ($module, @methods) = @_;
271 0         0 my $tb = $CLASS->builder;
272              
273 0         0 my $test = $tb->current_test;
274 0         0 my $name = "Checking methods in $module";
275             $tb->subtest( $name, sub {
276 0     0   0 for (@methods) {
277 0         0 $tb->ok($module->can($_), "$module has method $_");
278             }
279 0         0 });
280             }
281              
282             sub is_ref {
283 0     0 1 0 my ($var, $type, $name) = @_;
284 0         0 my $tb = $CLASS->builder;
285 0         0 my $test = $tb->current_test;
286 0         0 my $num = scalar @_;
287 0         0 my $err = "No ref type found";
288 0 0       0 if ($num == 1) {
    0          
    0          
289             # var only
290 0 0       0 if (! ref($var)) {
291 0         0 say "not ok $test - $err";
292 0         0 return 1;
293             }
294            
295 0         0 $tb->ok($var);
296             }
297             elsif ($num == 2) {
298             # var with name
299 0 0       0 if (! ref($var)) {
300 0         0 say "not ok $test - $err";
301 0         0 return 1;
302             }
303            
304 0         0 $tb->ok($var, $type);
305            
306             }
307             elsif ($num == 3) {
308 0 0       0 if (! ref($var)) {
309 0         0 say "not ok $test - $err";
310 0         0 return 1;
311             }
312 0 0       0 if (ref($var) ne uc($type)) {
313 0         0 say "not ok $test - Not of the same ref type";
314 0         0 return 1;
315             }
316              
317 0         0 $tb->ok(ref($var), $name);
318             }
319             }
320              
321             sub isa_ok {
322 0     0 1 0 my ($object, $class, $name) = @_;
323 0         0 my $tb = $CLASS->builder;
324 0   0     0 return $tb->ok( defined $object && $object->isa($class), $name );
325             }
326              
327             sub todo_start {
328 0     0 1 0 my ($message) = @_;
329 0         0 my $tb = $CLASS->builder;
330 0         0 return $tb->todo_start($message);
331             }
332              
333             sub todo_end {
334 0     0 1 0 my ($message) = @_;
335 0         0 my $tb = $CLASS->builder;
336 0         0 return $tb->todo_end($message);
337             }
338              
339             sub explain {
340 0     0 1 0 my ($a) = @_;
341 0         0 my $tb = $CLASS->builder;
342 0         0 $tb->explain($a);
343             }
344              
345             sub diag {
346 1     1 1 17 my ($msg) = @_;
347 1         21 my $tb = $CLASS->builder;
348              
349 1         28 $tb->diag($msg);
350             }
351              
352             sub methods {
353 0     0 1 0 my $class = shift;
354 0         0 my $tb = $CLASS->builder;
355 2     2   17 no strict 'refs';
  2         3  
  2         2313  
356 0 0       0 if (scalar keys %{"$class\::"} < 1) {
  0         0  
357 0         0 $tb->explain("methods(): Attempted method list on $class, but $class doesn't exist");
358 0         0 return 1;
359             }
360 0         0 my @m;
361 0         0 for (keys %{"$class\::"}) {
  0         0  
362 0 0       0 if (substr($_, -2, -1) eq ':') { push @m, "-> extends $_"; }
  0         0  
363 0         0 else { push @m, $_; }
364             }
365 0         0 return join "\n", @m;
366             }
367              
368             sub subtest {
369 0     0 1 0 my ($name, $subtest) = @_;
370 0         0 my $tb = $CLASS->builder;
371 0         0 $tb->subtest($name, $subtest);
372             }
373              
374             sub deep_keys {
375 0     0 0 0 my ($self, $hashref, $code, $args) = @_;
376 0         0 while (my ($k, $v) = each(%$hashref)) {
377 0 0       0 my @newargs = defined($args) ? @$args : ();
378 0         0 push(@newargs, $k);
379 0 0       0 if (ref($v) eq 'HASH') {
380 0         0 $CLASS->deep_keys($v, $code, \@newargs);
381             }
382             else {
383 0         0 $code->(@newargs);
384             }
385             }
386             }
387              
388             sub has_key {
389 0     0 1 0 my ($refvar, $key, $name) = @_;
390 0         0 my $tb = $CLASS->builder;
391 0 0       0 if (! ref($refvar)) {
392 0         0 $tb->skip('First parameter must be reference');
393 0         0 return 1;
394             }
395            
396 0 0       0 if (ref($refvar) eq 'HASH') {
    0          
397 0         0 my $match = 0;
398             $CLASS->deep_keys($refvar, sub {
399 0         0 $match = 1
400 0 0   0   0 if grep { $_ eq $key } @_;
401 0         0 });
402              
403 0 0       0 if ($match) { $tb->ok(1, $name); }
  0         0  
404 0         0 else { $tb->ok(0, $name); }
405             }
406             elsif (ref($refvar) eq 'ARRAY') {
407 0 0       0 if ( grep { $_ eq $key } @$refvar ) {
  0         0  
408 0         0 $tb->ok(1, $name);
409             }
410             else {
411 0         0 $tb->ok(0, $name);
412             }
413             }
414             }
415              
416             sub plan {
417 1     1 1 47 my $tb = $CLASS->builder;
418 1         23 $tb->plan(@_);
419             }
420              
421             sub is_passing {
422 0     0 1   my $tb = $CLASS->builder;
423 0           $tb->is_passing;
424             }
425              
426             sub level {
427 0     0 0   my $tb = $CLASS->builder;
428 0           $tb->level(@_);
429             }
430              
431             sub finfo {
432 0     0 0   my $tb = $CLASS->builder;
433 0           $tb->caller(@_);
434             }
435              
436             sub note {
437 0     0 1   my $tb = $CLASS->builder;
438 0           $tb->note(@_);
439             }
440              
441             sub count {
442 0     0 1   my ($v, $c, $name) = @_;
443 0           my $tb = $CLASS->builder;
444 0 0         if (! ref($v)) {
445 0           $CLASS->syntax_fail( "count(): First parameter must be a reference" );
446 0           return 1;
447             }
448             else {
449 0 0         if (! looks_like_number($c)) {
450 0           $CLASS->syntax_fail( "Can't match against a non-numeric value" );
451 0           return 1;
452             }
453            
454 0 0         if (ref($v) eq 'ARRAY') {
    0          
455 0           my $num = scalar @$v;
456 0 0 0       if ($num != $c) { $CLASS->fail($name||"count(): Number of elements do not match"); }
  0            
457 0   0       else { $tb->ok(1, $name||"count(): Number of elements match"); }
458             }
459             elsif (ref($v) eq 'HASH') {
460 0           my $num = scalar keys %$v;
461 0 0 0       if ($num != $c) { $CLASS->fail($name||"count(): Number of keys do not mach"); }
  0            
462 0   0       else { $tb->ok(1, $name||"count(): Number of keys match"); }
463             }
464             }
465             }
466              
467             sub fail {
468 0     0 0   my ($self, $message) = @_;
469 0           my $tb = $CLASS->builder;
470 0           $tb->ok(0, $message);
471             }
472              
473             sub syntax_fail {
474 0     0 0   my ($self, $message) = @_;
475 0           my $tb = $CLASS->builder;
476 0 0         if ($self->dieonsyntax) { $tb->ok(0, $message); }
  0            
477 0           else { $tb->skip($message); }
478             }
479              
480             sub done_testing {
481 0     0 0   my ($num) = @_;
482 0           my $tb = $CLASS->builder;
483            
484 0           $tb->finalize();
485 0           $tb->done_testing($num);
486             }
487              
488             =head1 TESTS
489              
490             =head2 is
491              
492             is ( $a, $b, {}, 'Name of test');
493              
494             Does C<$a> equal C<$b>? This particular test can match integers or strings.
495             Third parameter takes a hashref. Using this hashref you can make the test a little
496             more 'strict' by setting a type to check for.
497              
498             my $a = 1;
499             my $b = 'one';
500            
501             is ($a, $b, { type => 'Int' });
502            
503             The above will fail because it expects an integer, but C<$b> is a string.
504              
505             =head2 ok
506              
507             my $test = "World";
508             my $pass = 0;
509              
510             ok ( $test, $name ); # passes
511             ok ( $pass ); # fails
512              
513             Checks that the first parameter returns C. If not, it will fail.
514              
515             =head2 cmp_ok
516              
517             Evaluates the parameters using the operator specified as the second parameter.
518              
519             cmp_ok ( 'this', 'eq', 'that', 'Test Name' );
520             cmp_ok ( 1, '==', 2, 'Test Name' );
521              
522             =head2 like
523              
524             like( 'Hello, World!', qr/Hello/, 'Test Name');
525              
526             Searches the first parameter for the regex specified in the second. If it's found it will pass the test.
527              
528             =head2 unlike
529              
530             Similar to C, but the opposite.
531              
532             =head2 diff
533              
534             Checks the values of two references (HashRef or ArrayRef). If any are different the test will fail and you'll be able to see
535             the output of what C was expecting, and what it actually got
536              
537             my $a = { foo => 'bar' };
538             my $b = { baz => 'foo' };
539            
540             diff $a, $b, 'Test name'; # fail
541              
542             my $ary = [1, 2, 3];
543             my $ary2 = [1, 2, 3];
544            
545             diff $ary, $ary2, 'Test name'; # pass
546              
547             =head2 can_ok
548              
549             Finds out whether the specified module can call on certain methods.
550              
551             can_ok 'Foo' => qw/ this that them who what /;
552              
553             =head2 isa_ok
554              
555             Tests to see if the specified object returns the right class
556              
557             my $ob = Foo->new;
558             isa_ok $ob, 'Foo', 'Test Name';
559              
560             =head2 diag
561              
562             Pretty much the same as other Test libraries. Returns output that won't interrupt your tests.
563              
564             diag 'Boo!';
565              
566             =head2 methods
567              
568             Returns a string listing all the methods callable by a module.
569              
570             can_ok( Foo => ['test'] ) or diag methods('Foo');
571              
572             =head2 explain
573              
574             Returns a dump of an object (like a hash/arrayref).
575              
576             my $hash = {
577             a => 1,
578             b => 'foo',
579             c => 'baz'
580             };
581             diag explain $hash;
582              
583             Will return
584              
585             # {
586             # 'a' => 1,
587             # 'b' => 'foo',
588             # 'c' => 'baz'
589             # }
590              
591             =head2 use_ok
592              
593             Attempts to use the module given, or multiple modules if an arrayref is provided
594              
595             use_ok 'Foo';
596             use_ok [qw( Foo Foo::Bar Baz )];
597              
598             =head2 todo_start
599              
600             Signifies the beginning of todo tests
601              
602             todo_start("Starting todo tests");
603             # ...
604            
605             =head2 todo_end
606              
607             The end of the todo tests. Don't forget to call when you've finished your todo tests.
608              
609             todo_end("Finished todo tests");
610             todo_end();
611              
612             =head2 is_ref
613              
614             Checks to see if the value given is a true reference. You can go one step further and prove a reference type
615             to check against.
616              
617             my @non_ref qw(not a ref);
618             my $true_ref = [1, 2, 3];
619            
620             is_ref(@non_ref, 'Name of test');
621             is_ref($true_ref => 'HASH', 'Name of test'); # Checks to see if $true_ref returns a HASH
622              
623             =head2 subtest
624              
625             Create subtests within a test.
626              
627             use Test::Lite;
628              
629             use_ok 'Some::Module';
630            
631             subtest 'My test name' => sub {
632             ok ref({}), 'HASH' => 'Reference type is hash';
633             };
634              
635             subtest 'Another subtest' => sub {
636             my $ob = Some::Module->new;
637             isa_ok( $ob, 'Some::Module' => 'Matching class with object' );
638             };
639              
640             =head2 has_key
641              
642             Searches an ArrayRef or HashRef (deeply) for a specific element or key.
643              
644             my $hash = {
645             name => 'World',
646             foo => 'baz',
647             berry => {
648             fruit => {
649             melon => 'Yum!',
650             },
651             },
652             };
653              
654             has_key $hash, 'melon' => 'Found melon!';
655              
656             my $ary = [qw(this that there where who what)];
657              
658             has_key $ary, 'there' => 'Found "there" in arrayref';
659              
660             =head2 plan
661              
662             Declare how many tests you are going to run. This is not needed if you have included C
663              
664             use Test::Lite;
665              
666             plan tests => 2;
667             plan 'no_plan';
668             plan skip_all => 'reason';
669              
670             =head2 is_passing
671              
672             Detects whether the current test suite is passing.
673              
674             is_passing or diag "Uh-Oh. We're currently failing the test..."
675              
676             =head2 note
677              
678             Just prints text to output(), so it should only be displayed in verbose mode.
679              
680             note 'Some note to describe stuff';
681              
682             =head2 count
683              
684             Counts the number of keys from a hashref, or elements from an arrayref and matches them against the expected value.
685              
686             my $h = {
687             foo => 'bar',
688             baz => 'foo'
689             };
690             count $h, 2 => 'Expecting 2 keys in hash';
691              
692             my $a = [1, 2, 3, 4];
693             count $a, $a->[3] => "Expecting $a->[3] elements from array";
694              
695             =head2 extended
696              
697             Searches the module deeply for extended modules. ie: When you C or C in most OOP frameworks.
698              
699             package Foo;
700            
701             use base qw/
702             Foo::Baz
703             Foo::Baz::Foobar
704             Foo::Baz::Foobar::Frag
705             /;
706            
707             1;
708              
709             # t/01-extends.t
710              
711             use Test::Lite;
712            
713             use_ok 'Foo';
714             extended 'Foo' => qw/
715             Foo::Baz
716             Foo::Baz::Foobar::Frag
717             /;
718            
719             done_testing;
720              
721             =head1 AUTHOR
722              
723             Brad Haywood
724              
725             =head1 LICENSE
726              
727             You may distribute this code under the same terms as Perl itself.
728              
729             =cut
730              
731             1;