Test-Roo-1.002/000755 000765 000024 00000000000 12220622620 013414 5ustar00davidstaff000000 000000 Test-Roo-1.002/Changes000644 000765 000024 00000002775 12220622620 014722 0ustar00davidstaff000000 000000 Revision history for Test-Roo 1.002 2013-09-25 14:03:55 America/New_York [FIXES] - Compile test could hang on Windows [PREREQS] - Dropped configure_requires for ExtUtils::MakeMaker to 6.17 1.001 2013-09-11 10:13:42 America/New_York [CHANGED] - modifiers on 'each_test' are now included in the subtest block so that any tests run in the modifiers are listed with other tests in the block [DOCUMENTED] - Enhanced SYNOPSIS 1.000 2013-03-08 18:59:20 America/New_York [INCOMPATIBLE CHANGES] - importing Test::Roo makes the current class a subclass of Test::Roo::Class; all methods moved there instead of being exported - Test::Roo now only exports 'test' and 'run_me' - done_testing is never called automatically [ADDED] - Test::Roo::Class has 'run_tests' method for creating and running tests; can be called as object or class method - Test::Roo::Class has lazy 'description' attribute for generating descriptions from test attributes - Added 'each_test' hook for global actions before/after every test [DOCUMENTED] - Includes Test::Roo::Cookbook with examples - Test::Roo docs explain differences from Test::Routine 0.002 2013-02-25 14:25:09 America/New_York [ADDED] - Refactored test run code into a new 'run_me' method to allow manual control over test runs, including multiple, parameterized runs. 0.001 2013-02-25 12:23:27 America/New_York - First release Test-Roo-1.002/CONTRIBUTING000644 000765 000024 00000004234 12220622620 015251 0ustar00davidstaff000000 000000 ## HOW TO CONTRIBUTE Thank you for considering contributing to this distribution. This file contains instructions that will help you work with the source code. The distribution is managed with Dist::Zilla. This means than many of the usual files you might expect are not in the repository, but are generated at release time (e.g. Makefile.PL). ### Getting dependencies See the included `cpanfile` file for a list of dependencies. If you have App::cpanminus 1.6 or later installed, you can use `cpanm` to satisfy dependencies like this: $ cpanm --installdeps . Otherwise, you can install Module::CPANfile 1.0002 or later and then satisfy dependencies with the regular `cpan` client and `cpanfile-dump`: $ cpan `cpanfile-dump` ### Running tests You can run tests directly using the `prove` tool: $ prove -l $ prove -lv t/some_test_file.t For most distributions, `prove` is entirely sufficent for you to test any patches you have. ### Code style and tidying Please try to match any existing coding style. If there is a `.perltidyrc` file, please install Perl::Tidy and use perltidy before submitting patches. If there is a `tidyall.ini` file, you can also install Code::TidyAll and run `tidyall` on a file or `tidyall -a` to tidy all files. ### Patching documentation Much of the documentation Pod is generated at release time. Depending on the distribution, some documentation may be written in a Pod dialect called WikiDoc. (See Pod::WikiDoc on CPAN.) If you would like to submit a documentation edit, please limit yourself to the documentation you see. If you see typos or documentation issues in the generated docs, please email or open a bug ticket instead of patching. ### Learning Dist::Zilla Dist::Zilla is a very powerful authoring tool, but requires a number of author-specific plugins. If you would like to use it for contributing, install it from CPAN, then run one of the following commands, depending on your CPAN client: $ cpan `dzil authordeps` $ dzil authordeps | cpanm Once installed, here are some dzil commands you might try: $ dzil build $ dzil test $ dzil xtest You can learn more about Dist::Zilla at http://dzil.org/ Test-Roo-1.002/cpanfile000644 000765 000024 00000001737 12220622620 015130 0ustar00davidstaff000000 000000 requires "Moo" => "1.000008"; requires "MooX::Types::MooseLike::Base" => "0"; requires "Sub::Install" => "0"; requires "Test::More" => "0.96"; requires "perl" => "5.008001"; requires "strictures" => "0"; recommends "bareword::filehandles" => "0"; recommends "indirect" => "0"; recommends "multidimensional" => "0"; on 'test' => sub { requires "Capture::Tiny" => "0"; requires "ExtUtils::MakeMaker" => "0"; requires "File::Spec" => "0"; requires "File::Spec::Functions" => "0"; requires "File::Temp" => "0"; requires "IO::Handle" => "0"; requires "IPC::Open3" => "0"; requires "List::Util" => "0"; requires "Test::More" => "0"; requires "lib" => "0"; requires "strict" => "0"; requires "warnings" => "0"; }; on 'configure' => sub { requires "ExtUtils::MakeMaker" => "6.17"; }; on 'develop' => sub { requires "Pod::Coverage::TrustPod" => "0"; requires "Test::CPAN::Meta" => "0"; requires "Test::Pod" => "1.41"; requires "Test::Pod::Coverage" => "1.08"; }; Test-Roo-1.002/dist.ini000644 000765 000024 00000000554 12220622620 015064 0ustar00davidstaff000000 000000 name = Test-Roo author = David Golden license = Apache_2_0 copyright_holder = David Golden copyright_year = 2013 [@DAGOLDEN] :version = 0.053 stopwords = subtest [Prereqs] Moo = 1.000008 Test::More = 0.96 [Prereqs / Recommends] indirect = 0 multidimensional = 0 bareword::filehandles = 0 [RemovePrereqs] remove = RoleNotFoundAnywhere Test-Roo-1.002/examples/000755 000765 000024 00000000000 12220622620 015232 5ustar00davidstaff000000 000000 Test-Roo-1.002/lib/000755 000765 000024 00000000000 12220622620 014162 5ustar00davidstaff000000 000000 Test-Roo-1.002/LICENSE000644 000765 000024 00000026354 12220622620 014433 0ustar00davidstaff000000 000000 This software is Copyright (c) 2013 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Test-Roo-1.002/Makefile.PL000644 000765 000024 00000003372 12220622620 015373 0ustar00davidstaff000000 000000 use strict; use warnings; use 5.008001; use ExtUtils::MakeMaker 6.17; my %WriteMakefileArgs = ( "ABSTRACT" => "Composable, reusable tests with roles and Moo", "AUTHOR" => "David Golden ", "BUILD_REQUIRES" => {}, "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => "6.17" }, "DISTNAME" => "Test-Roo", "EXE_FILES" => [], "LICENSE" => "apache", "NAME" => "Test::Roo", "PREREQ_PM" => { "Moo" => "1.000008", "MooX::Types::MooseLike::Base" => 0, "Sub::Install" => 0, "Test::More" => "0.96", "strictures" => 0 }, "TEST_REQUIRES" => { "Capture::Tiny" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "File::Spec::Functions" => 0, "File::Temp" => 0, "IO::Handle" => 0, "IPC::Open3" => 0, "List::Util" => 0, "Test::More" => 0, "lib" => 0, "strict" => 0, "warnings" => 0 }, "VERSION" => "1.002", "test" => { "TESTS" => "t/*.t" } ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { my $tr = delete $WriteMakefileArgs{TEST_REQUIRES}; my $br = $WriteMakefileArgs{BUILD_REQUIRES}; for my $mod ( keys %$tr ) { if ( exists $br->{$mod} ) { $br->{$mod} = $tr->{$mod} if $tr->{$mod} > $br->{$mod}; } else { $br->{$mod} = $tr->{$mod}; } } } unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) { my $br = delete $WriteMakefileArgs{BUILD_REQUIRES}; my $pp = $WriteMakefileArgs{PREREQ_PM}; for my $mod ( keys %$br ) { if ( exists $pp->{$mod} ) { $pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod}; } else { $pp->{$mod} = $br->{$mod}; } } } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Test-Roo-1.002/MANIFEST000644 000765 000024 00000002427 12220622620 014552 0ustar00davidstaff000000 000000 CONTRIBUTING Changes LICENSE MANIFEST META.json META.yml Makefile.PL README cpanfile dist.ini examples/cookbook/hookable_test.t examples/cookbook/lib/CorpusCheck.pm examples/cookbook/lib/IteratorTest.pm examples/cookbook/single_file.t examples/cookbook/sqlite-confirm.t examples/cookbook/sqlite.t examples/cookbook/standalone.t examples/cookbook/test-pcr.pl examples/cookbook/test-pir.pl examples/cookbook/with_tempd.t examples/cookbook/wrapped.t examples/synopsis/README examples/synopsis/t/lib/ObjectCreation.pm examples/synopsis/t/test.t lib/Test/Roo.pm lib/Test/Roo/Class.pm lib/Test/Roo/Cookbook.pm lib/Test/Roo/Role.pm perlcritic.rc t/00-compile.t t/00-report-prereqs.t t/basic.t t/bin/custom-order.pl t/bin/main-order.pl t/bin/not-strict.pl t/bin/role-last.pl t/bin/role-not-found.pl t/bin/skip-all.pl t/bin/skip-in-role.pl t/bin/unsatisfied.pl t/each_test.t t/exceptions.t t/lib/ClassConstructor.pm t/lib/EachTest.pm t/lib/LastTest.pm t/lib/RequiresFixture.pm t/lib/SetTear.pm t/lib/Skipper.pm t/multi.t t/object.t t/ordering.t t/role-multi.t t/role.t t/setup-teardown.t t/test-more-args.t tidyall.ini xt/author/critic.t xt/author/pod-spell.t xt/release/distmeta.t xt/release/minimum-version.t xt/release/pod-coverage.t xt/release/pod-syntax.t xt/release/portability.t xt/release/test-version.t Test-Roo-1.002/META.json000644 000765 000024 00000005354 12220622620 015044 0ustar00davidstaff000000 000000 { "abstract" : "Composable, reusable tests with roles and Moo", "author" : [ "David Golden " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 4.300039, CPAN::Meta::Converter version 2.132620", "license" : [ "apache_2_0" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Test-Roo", "no_index" : { "directory" : [ "t", "xt", "examples", "corpus" ], "package" : [ "DB" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "6.17" } }, "develop" : { "requires" : { "Pod::Coverage::TrustPod" : "0", "Test::CPAN::Meta" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08" } }, "runtime" : { "recommends" : { "bareword::filehandles" : "0", "indirect" : "0", "multidimensional" : "0" }, "requires" : { "Moo" : "1.000008", "MooX::Types::MooseLike::Base" : "0", "Sub::Install" : "0", "Test::More" : "0.96", "perl" : "5.008001", "strictures" : "0" } }, "test" : { "requires" : { "Capture::Tiny" : "0", "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "File::Spec::Functions" : "0", "File::Temp" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "List::Util" : "0", "Test::More" : "0", "lib" : "0", "strict" : "0", "warnings" : "0" } } }, "provides" : { "Test::Roo" : { "file" : "lib/Test/Roo.pm", "version" : "1.002" }, "Test::Roo::Class" : { "file" : "lib/Test/Roo/Class.pm", "version" : "1.002" }, "Test::Roo::Cookbook" : { "file" : "lib/Test/Roo/Cookbook.pm", "version" : "1.002" }, "Test::Roo::Role" : { "file" : "lib/Test/Roo/Role.pm", "version" : "1.002" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/dagolden/Test-Roo/issues" }, "homepage" : "https://github.com/dagolden/Test-Roo", "repository" : { "type" : "git", "url" : "https://github.com/dagolden/Test-Roo.git", "web" : "https://github.com/dagolden/Test-Roo" } }, "version" : "1.002", "x_authority" : "cpan:DAGOLDEN", "x_contributors" : [ "Diab Jerius " ] } Test-Roo-1.002/META.yml000644 000765 000024 00000002645 12220622620 014674 0ustar00davidstaff000000 000000 --- abstract: 'Composable, reusable tests with roles and Moo' author: - 'David Golden ' build_requires: Capture::Tiny: 0 ExtUtils::MakeMaker: 0 File::Spec: 0 File::Spec::Functions: 0 File::Temp: 0 IO::Handle: 0 IPC::Open3: 0 List::Util: 0 Test::More: 0 lib: 0 strict: 0 warnings: 0 configure_requires: ExtUtils::MakeMaker: 6.17 dynamic_config: 0 generated_by: 'Dist::Zilla version 4.300039, CPAN::Meta::Converter version 2.132620' license: apache meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Test-Roo no_index: directory: - t - xt - examples - corpus package: - DB provides: Test::Roo: file: lib/Test/Roo.pm version: 1.002 Test::Roo::Class: file: lib/Test/Roo/Class.pm version: 1.002 Test::Roo::Cookbook: file: lib/Test/Roo/Cookbook.pm version: 1.002 Test::Roo::Role: file: lib/Test/Roo/Role.pm version: 1.002 recommends: bareword::filehandles: 0 indirect: 0 multidimensional: 0 requires: Moo: 1.000008 MooX::Types::MooseLike::Base: 0 Sub::Install: 0 Test::More: 0.96 perl: 5.008001 strictures: 0 resources: bugtracker: https://github.com/dagolden/Test-Roo/issues homepage: https://github.com/dagolden/Test-Roo repository: https://github.com/dagolden/Test-Roo.git version: 1.002 x_authority: cpan:DAGOLDEN x_contributors: - 'Diab Jerius ' Test-Roo-1.002/perlcritic.rc000644 000765 000024 00000001417 12220622620 016105 0ustar00davidstaff000000 000000 severity = 5 verbose = 8 [Variables::ProhibitPunctuationVars] allow = $@ $! [TestingAndDebugging::ProhibitNoStrict] allow = refs # Turn these off [-BuiltinFunctions::ProhibitStringyEval] [-ControlStructures::ProhibitPostfixControls] [-ControlStructures::ProhibitUnlessBlocks] [-Documentation::RequirePodSections] [-InputOutput::ProhibitInteractiveTest] [-References::ProhibitDoubleSigils] [-RegularExpressions::RequireExtendedFormatting] [-InputOutput::ProhibitTwoArgOpen] [-Modules::ProhibitEvilModules] # Turn this on [-Lax::ProhibitStringyEval::ExceptForRequire] # Customize [Lax::RequireExplicitPackage::ExceptForPragmata] allowed_pragmata = diagnostics feature perlversion strict warnings strictures [TestingAndDebugging::RequireUseStrict] equivalent_modules = strictures Test-Roo-1.002/README000644 000765 000024 00000022501 12220622620 014274 0ustar00davidstaff000000 000000 NAME Test::Roo - Composable, reusable tests with roles and Moo VERSION version 1.002 SYNOPSIS Define test behaviors and required fixtures in a role: # t/lib/ObjectCreation.pm package ObjectCreation; use Test::Roo::Role; # loads Moo::Role and Test::More requires 'class'; # we need this fixture test 'object creation' => sub { my $self = shift; require_ok( $self->class ); my $obj = new_ok( $self->class ); }; 1; Provide fixtures and run tests from the .t file: # t/test.t use Test::Roo; # loads Moo and Test::More use lib 't/lib'; # provide the fixture has class => ( is => 'ro', default => sub { "Digest::MD5" }, ); # specify behaviors to test with 'ObjectCreation'; # give our subtests a pretty label sub _build_description { "Testing " . shift->class } # run the test with default fixture run_me; # run the test with different fixture run_me( { class => "Digest::SHA1" } ); done_testing; Result: $ prove -lv t t/test.t .. ok 1 - require Digest::MD5; ok 2 - The object isa Digest::MD5 1..2 ok 1 - object creation 1..1 ok 1 - Testing Digest::MD5 ok 1 - require Digest::SHA1; ok 2 - The object isa Digest::SHA1 1..2 ok 1 - object creation 1..1 ok 2 - Testing Digest::SHA1 1..2 ok All tests successful. Files=1, Tests=2, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.06 cusr 0.00 csys = 0.09 CPU) Result: PASS DESCRIPTION This module allows you to compose Test::More tests from roles. It is inspired by the excellent Test::Routine module, but uses Moo instead of Moose. This gives most of the benefits without the need for Moose as a test dependency. Test files are Moo classes. You can define any needed test fixtures as Moo attributes. You define tests as method modifiers -- similar in concept to "subtest" in Test::More, but your test method will be passed the test object for access to fixture attributes. You may compose any Moo::Role into your test to define attributes, require particular methods, or define tests. This means that you can isolate test *behaviors* into roles which require certain test *fixtures* in order to run. Your main test file will provide the fixtures and compose the roles to run. This makes it easy to reuse test behaviors. For example, if you are creating tests for Awesome::Module, you could create the test behaviors as Awesome::Module::Test::Role and distribute it with your module. If another distribution subclasses Awesome::Module, it can compose the Awesome::Module::Test::Role behavior for its own tests. No more copying and pasting tests from a super class! Superclasses define and share their tests. Subclasses provide their own fixtures and run the tests. USAGE Importing Test::Roo also loads Moo (which gives you strictures with fatal warnings and other goodies) and makes the current package a subclass of Test::Roo::Class. Importing also loads Test::More. No test plan is used. The "done_testing" function must be used at the end of every test file. Any import arguments are passed through to Test::More's "import" method. See also Test::Roo::Role for test role usage. Creating fixtures You can create fixtures with normal Moo syntax. You can even make them lazy if you want: has fixture => ( is => 'lazy' ); sub _build_fixture { ... } This becomes really useful with Test::Roo::Role. A role could define the attribute and require the builder method to be provided by the main test class. Composing test roles You can use roles to define units of test behavior and then compose them into your test class using the "with" function. Test roles may define attributes, declare tests, require certain methods and anything else you can regularly do with roles. use Test::Roo; with 'MyTestRole1', 'MyTestRole2'; See Test::Roo::Role and the Test::Roo::Cookbook for details and examples. Setup and teardown You can add method modifiers around the "setup" and "teardown" methods and these will be run before tests begin and after tests finish (respectively). before setup => sub { ... }; after teardown => sub { ... }; You can also add method modifiers around "each_test", which will be run before and after every individual test. You could use these to prepare or reset a fixture. has fixture => ( is => 'lazy, clearer => 1, predicate => 1 ); after each_test => sub { shift->clear_fixture }; Roles may also modify "setup", "teardown", and "each_test", so the order that modifiers will be called will depend on when roles are composed. Be careful with "each_test", though, because the global effect may make composition more fragile. You can call test functions in modifiers. For example, you could confirm that something has been set up or cleaned up. before each_test => sub { ok( ! shift->has_fixture ) }; Running tests The simplest way to use Test::Roo with a single .t file is to let the "main" package be the test class and call "run_me" in it: # t/test.t use Test::Roo; # loads Moo and Test::More has class => ( is => 'ro', default => sub { "Digest::MD5" }, ); test 'load class' => sub { my $self = shift; require_ok( $self->class ); } run_me; done_testing; Calling "run_me(@args)" is equivalent to calling "__PACKAGE__->run_tests(@args)" and runs tests for the current package. You may specify an optional description or hash reference of constructor arguments to customize the test object: run_me( "load MD5" ); run_me( { class => "Digest::MD5" } ); run_me( "load MD5", { class => "Digest::MD5" } ); See Test::Roo::Class for more about the "run_tests" method. Alternatively, you can create a separate package (in the test file or in a separate .pm file) and run tests explicitly on that class. # t/test.t package MyTest; use Test::Roo; use lib 't/lib'; has class => ( is => 'ro', required => 1, ); with 'MyTestRole'; package main; use strictures; use Test::More; for my $c ( qw/Digest::MD5 Digest::SHA/ ) { MyTest->run_tests("Testing $c", { class => $c } ); } done_testing; EXPORTED FUNCTIONS Loading Test::Roo exports subroutines into the calling package to declare and run tests. test test $label => sub { ... }; The "test" function adds a subtest. The code reference will be called with the test object as its only argument. Tests are run in the order declared, so the order of tests from roles will depend on when they are composed relative to other test declarations. run_me run_me; run_me( $description ); run_me( $init_args ); run_me( $description, $init_args ); The "run_me" function calls the "run_tests" method on the current package and passes all arguments to that method. It takes a description and/or a hash reference of constructor arguments. DIFFERENCES FROM TEST::ROUTINE While this module was inspired by Test::Routine, it is not a drop-in replacement. Here is an overview of major differences: * Test::Roo uses Moo; Test::Routine uses Moose * Loading Test::Roo makes the importing package a class; in Test::Routine it becomes a role * Loading Test::Roo loads Test::More; Test::Routine does not * In Test::Roo, "run_test" is a method; in Test::Routine it is a function and takes arguments in a different order * In Test::Roo, all role composition must be explicit using "with"; in Test::Routine, the "run_tests" command can also compose roles * In Test::Roo, test blocks become method modifiers hooked on an empty method; in Test::Routine, they become methods run via introspection * In Test::Roo, setup and teardown are done by modifying "setup" and "teardown" methods; in Test::Routine they are done by modifying "run_test" SUPPORT Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at . You will be notified automatically of any progress on your issue. Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. git clone https://github.com/dagolden/Test-Roo.git AUTHOR David Golden CONTRIBUTOR Diab Jerius COPYRIGHT AND LICENSE This software is Copyright (c) 2013 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 Test-Roo-1.002/t/000755 000765 000024 00000000000 12220622620 013657 5ustar00davidstaff000000 000000 Test-Roo-1.002/tidyall.ini000644 000765 000024 00000000240 12220622620 015553 0ustar00davidstaff000000 000000 ; Install Code::TidyAll ; run "tidyall -a" to tidy all files ; run "tidyall -g" to tidy only files modified from git [PerlTidy] select = {lib,t}/**/*.{pl,pm,t} Test-Roo-1.002/xt/000755 000765 000024 00000000000 12220622620 014047 5ustar00davidstaff000000 000000 Test-Roo-1.002/xt/author/000755 000765 000024 00000000000 12220622620 015351 5ustar00davidstaff000000 000000 Test-Roo-1.002/xt/release/000755 000765 000024 00000000000 12220622620 015467 5ustar00davidstaff000000 000000 Test-Roo-1.002/xt/release/distmeta.t000644 000765 000024 00000000217 12220622620 017466 0ustar00davidstaff000000 000000 #!perl use Test::More; eval "use Test::CPAN::Meta"; plan skip_all => "Test::CPAN::Meta required for testing META.yml" if $@; meta_yaml_ok(); Test-Roo-1.002/xt/release/minimum-version.t000644 000765 000024 00000000266 12220622620 021016 0ustar00davidstaff000000 000000 #!perl use Test::More; eval "use Test::MinimumVersion"; plan skip_all => "Test::MinimumVersion required for testing minimum versions" if $@; all_minimum_version_ok( qq{5.010} ); Test-Roo-1.002/xt/release/pod-coverage.t000644 000765 000024 00000000527 12220622620 020233 0ustar00davidstaff000000 000000 #!perl use Test::More; eval "use Test::Pod::Coverage 1.08"; plan skip_all => "Test::Pod::Coverage 1.08 required for testing POD coverage" if $@; eval "use Pod::Coverage::TrustPod"; plan skip_all => "Pod::Coverage::TrustPod required for testing POD coverage" if $@; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); Test-Roo-1.002/xt/release/pod-syntax.t000644 000765 000024 00000000212 12220622620 017755 0ustar00davidstaff000000 000000 #!perl use Test::More; eval "use Test::Pod 1.41"; plan skip_all => "Test::Pod 1.41 required for testing POD" if $@; all_pod_files_ok(); Test-Roo-1.002/xt/release/portability.t000644 000765 000024 00000000332 12220622620 020214 0ustar00davidstaff000000 000000 #!perl use strict; use warnings; use Test::More; eval 'use Test::Portability::Files'; plan skip_all => 'Test::Portability::Files required for testing portability' if $@; options(test_one_dot => 0); run_tests(); Test-Roo-1.002/xt/release/test-version.t000644 000765 000024 00000000643 12220622620 020321 0ustar00davidstaff000000 000000 use strict; use warnings; use Test::More; # generated by Dist::Zilla::Plugin::Test::Version 0.002004 BEGIN { eval "use Test::Version; 1;" or die $@; } my @imports = ( 'version_all_ok' ); my $params = { is_strict => 0, has_version => 1, }; push @imports, $params if version->parse( $Test::Version::VERSION ) >= version->parse('1.002'); Test::Version->import(@imports); version_all_ok; done_testing; Test-Roo-1.002/xt/author/critic.t000644 000765 000024 00000000435 12220622620 017015 0ustar00davidstaff000000 000000 #!perl use strict; use warnings; use Test::More; use English qw(-no_match_vars); eval "use Test::Perl::Critic"; plan skip_all => 'Test::Perl::Critic required to criticise code' if $@; Test::Perl::Critic->import( -profile => "perlcritic.rc" ) if -e "perlcritic.rc"; all_critic_ok(); Test-Roo-1.002/xt/author/pod-spell.t000644 000765 000024 00000000470 12220622620 017436 0ustar00davidstaff000000 000000 use strict; use warnings; use Test::More; # generated by Dist::Zilla::Plugin::Test::PodSpelling 2.006001 use Test::Spelling 0.12; use Pod::Wordlist; add_stopwords(); all_pod_files_spelling_ok( qw( bin lib ) ); __DATA__ subtest David Golden dagolden Diab Jerius djerius lib Test Roo Class Role Cookbook Test-Roo-1.002/t/00-compile.t000644 000765 000024 00000002057 12220622620 015715 0ustar00davidstaff000000 000000 use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.033 use Test::More tests => 4 + ($ENV{AUTHOR_TESTING} ? 1 : 0); my @module_files = ( 'Test/Roo.pm', 'Test/Roo/Class.pm', 'Test/Roo/Cookbook.pm', 'Test/Roo/Role.pm' ); # fake home for cpan-testers use File::Temp; local $ENV{HOME} = File::Temp::tempdir( CLEANUP => 1 ); use File::Spec; use IPC::Open3; use IO::Handle; my @warnings; for my $lib (@module_files) { # see L open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my $stderr = IO::Handle->new; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, '-Mblib', '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($? >> 8, 0, "$lib loaded ok"); if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } is(scalar(@warnings), 0, 'no warnings found') if $ENV{AUTHOR_TESTING}; Test-Roo-1.002/t/00-report-prereqs.t000644 000765 000024 00000003266 12220622620 017262 0ustar00davidstaff000000 000000 #!perl use strict; use warnings; use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec::Functions; use List::Util qw/max/; my @modules = qw( Capture::Tiny ExtUtils::MakeMaker File::Spec File::Spec::Functions File::Temp IO::Handle IPC::Open3 List::Util Moo MooX::Types::MooseLike::Base Sub::Install Test::More bareword::filehandles indirect lib multidimensional perl strict strictures warnings ); # replace modules with dynamic results from MYMETA.json if we can # (hide CPAN::Meta from prereq scanner) my $cpan_meta = "CPAN::Meta"; if ( -f "MYMETA.json" && eval "require $cpan_meta" ) { ## no critic if ( my $meta = eval { CPAN::Meta->load_file("MYMETA.json") } ) { my $prereqs = $meta->prereqs; delete $prereqs->{develop}; my %uniq = map {$_ => 1} map { keys %$_ } map { values %$_ } values %$prereqs; $uniq{$_} = 1 for @modules; # don't lose any static ones @modules = sort keys %uniq; } } my @reports = [qw/Version Module/]; for my $mod ( @modules ) { next if $mod eq 'perl'; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e catfile($_, $file) } @INC; if ( $prefix ) { my $ver = MM->parse_version( catfile($prefix, $file) ); $ver = "undef" unless defined $ver; # Newer MM should do this anyway push @reports, [$ver, $mod]; } else { push @reports, ["missing", $mod]; } } if ( @reports ) { my $vl = max map { length $_->[0] } @reports; my $ml = max map { length $_->[1] } @reports; splice @reports, 1, 0, ["-" x $vl, "-" x $ml]; diag "Prerequisite Report:\n", map {sprintf(" %*s %*s\n",$vl,$_->[0],-$ml,$_->[1])} @reports; } pass; # vim: ts=2 sts=2 sw=2 et: Test-Roo-1.002/t/basic.t000644 000765 000024 00000000706 12220622620 015130 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo; has fixture => ( is => 'ro', default => sub { "hello world" }, ); test try_me => sub { my $self = shift; like( $self->fixture, qr/hello world/, "saw fixture" ); }; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/bin/000755 000765 000024 00000000000 12220622620 014427 5ustar00davidstaff000000 000000 Test-Roo-1.002/t/each_test.t000644 000765 000024 00000001013 12220622620 015776 0ustar00davidstaff000000 000000 use Test::Roo; use lib 't/lib'; with 'EachTest'; sub _build_counter { return 0 } before each_test => sub { my $self = shift; pass("starting before main modifier"); $self->counter( $self->counter + 1 ); }; after each_test => sub { my $self = shift; $self->counter( $self->counter - 1 ); pass("finishing after main modifier"); }; test 'is two' => sub { is( shift->counter, 2, "counter is 2" ) }; test 'still two' => sub { is( shift->counter, 2, "counter is still 2" ) }; run_me; done_testing; Test-Roo-1.002/t/exceptions.t000644 000765 000024 00000003005 12220622620 016223 0ustar00davidstaff000000 000000 use 5.008001; use Test::More 0.96; use Capture::Tiny qw/capture/; use lib 't/lib'; my @cases = ( { label => "missing role", file => "t/bin/role-not-found.pl", expect => qr/Can't \S+ RoleNotFoundAnywhere\.pm in \@INC/, }, { label => "requires not satisfied", file => "t/bin/unsatisfied.pl", expect => qr/Can't apply RequiresFixture to main/, }, { label => "Test::Roo loads strictures", file => "t/bin/not-strict.pl", expect => qr/requires explicit package name/, }, { label => "skip_all respected", file => "t/bin/skip-all.pl", expect => qr/We just want to skip/, exit_ok => 1, stdout => 1, }, { label => "skip_all respected in role", file => "t/bin/skip-in-role.pl", expect => qr/We just want to skip/, exit_ok => 1, stdout => 1, }, ); for my $c (@cases) { my ( $output, $error, $rc ) = capture { system( $^X, $c->{file} ) }; subtest $c->{label} => sub { if ( $c->{exit_ok} ) { ok( !$rc, "exit ok" ); } else { ok( $rc, "nonzero exit" ); } like( $c->{stdout} ? $output : $error, $c->{expect}, "exception text" ); }; } done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/lib/000755 000765 000024 00000000000 12220622620 014425 5ustar00davidstaff000000 000000 Test-Roo-1.002/t/multi.t000644 000765 000024 00000001353 12220622620 015200 0ustar00davidstaff000000 000000 use 5.008001; package MyTest; use Test::Roo; has phrase => ( is => 'ro', required => 1, ); has regex => ( is => 'ro', default => sub { qr/world/i }, ); sub _build_description { return shift->phrase; } test try_me => sub { my $self = shift; like( $self->phrase, $self->regex, "phrase matched regex" ); }; package main; use strictures; use Test::More; my @phrases = ( 'hello world', 'goodbye world', ); for my $p (@phrases) { MyTest->run_tests( { phrase => $p } ); } done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/object.t000644 000765 000024 00000001114 12220622620 015307 0ustar00davidstaff000000 000000 use 5.008001; package MyTest; use Test::Roo; has fixture => ( is => 'ro', default => sub { "hello world" }, ); test try_me => sub { my $self = shift; like( $self->fixture, qr/hello world/, "saw fixture" ); }; package main; use strictures; use Test::More; my $obj = MyTest->new; $obj->run_tests; $obj->run_tests("with description"); done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/ordering.t000644 000765 000024 00000001701 12220622620 015654 0ustar00davidstaff000000 000000 use 5.008001; use Test::More 0.96; use Capture::Tiny qw/capture/; use lib 't/lib'; my @cases = ( { label => "main tests", file => "t/bin/main-order.pl", expect => qr/first_test.*?second_test/ms, }, { label => "role vs main", file => "t/bin/role-last.pl", expect => qr/in_main.*?in_role/ms, }, { label => "force role first", file => "t/bin/custom-order.pl", expect => qr/in_role.*?in_main/ms, }, ); for my $c (@cases) { my ( $output, $error, $rc ) = capture { system( $^X, $c->{file} ) }; subtest $c->{label} => sub { ok( !$rc, "zero exit" ); like( $output, $c->{expect}, "expected text" ); }; } done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/role-multi.t000644 000765 000024 00000001007 12220622620 016133 0ustar00davidstaff000000 000000 use 5.008001; package MyTest; use Test::Roo; use lib 't/lib'; has class => ( is => 'ro', required => 1, ); with 'ClassConstructor'; package main; use strictures; use Test::More; for my $c (qw/Digest::MD5 Math::BigInt/) { MyTest->run_tests( $c, { class => $c } ); } done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/role.t000644 000765 000024 00000000607 12220622620 015010 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo; use lib 't/lib'; has fixture => ( is => 'ro', default => sub { "hello world" }, ); with qw/RequiresFixture/; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/setup-teardown.t000644 000765 000024 00000000665 12220622620 017034 0ustar00davidstaff000000 000000 use Test::Roo; use lib 't/lib'; with 'SetTear'; after setup => sub { # intentionally *after* to check role my $self = shift; ok( $self->tempdir, "got tempdir" ); ok( $self->tempname, "got tempname" ); }; after teardown => sub { my $self = shift; is( $self->tempdir, undef, "tempdir cleared" ); ok( !-e $self->tempname, "tempdir doesn't exist" ); }; test 'stub test' => sub { ok(1) }; run_me; done_testing; Test-Roo-1.002/t/test-more-args.t000644 000765 000024 00000001140 12220622620 016711 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo import => [qw/like done_testing/]; has fixture => ( is => 'ro', default => sub { "hello world" }, ); test try_me => sub { my $self = shift; like( $self->fixture, qr/hello world/, "saw fixture" ); eval { fail("fail() called") }; like( $@, qr/undefined subroutine/i, "Not all Test::More functions imported" ); }; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/lib/ClassConstructor.pm000644 000765 000024 00000000306 12220622620 020275 0ustar00davidstaff000000 000000 package ClassConstructor; use Test::Roo::Role; requires 'class'; test 'object creation' => sub { my $self = shift; require_ok( $self->class ); my $obj = new_ok( $self->class ); }; 1; Test-Roo-1.002/t/lib/EachTest.pm000644 000765 000024 00000000760 12220622620 016466 0ustar00davidstaff000000 000000 package EachTest; use Test::Roo::Role; has counter => ( is => 'rw', lazy => 1, builder => 1, ); requires '_build_counter'; before each_test => sub { my $self = shift; pass("starting before module modifier"); $self->counter( $self->counter + 1 ); }; after each_test => sub { my $self = shift; $self->counter( $self->counter - 1 ); pass("finishing after module modifier"); }; test 'positive' => sub { ok( shift->counter, "counter positive" ) }; 1; Test-Roo-1.002/t/lib/LastTest.pm000644 000765 000024 00000000146 12220622620 016527 0ustar00davidstaff000000 000000 use 5.008001; package LastTest; use Test::Roo::Role; test in_role => sub { pass("role"); }; 1; Test-Roo-1.002/t/lib/RequiresFixture.pm000644 000765 000024 00000000304 12220622620 020126 0ustar00davidstaff000000 000000 use 5.008001; package RequiresFixture; use Test::Roo::Role; requires 'fixture'; test try_me => sub { my $self = shift; my $fixture = $self->fixture; ok( ($fixture) x 2 ); }; 1; Test-Roo-1.002/t/lib/SetTear.pm000644 000765 000024 00000000520 12220622620 016327 0ustar00davidstaff000000 000000 package SetTear; use Test::Roo::Role; use File::Temp; has tempdir => ( is => 'rw', ); has tempname => ( is => 'rw', ); before setup => sub { my $self = shift; $self->tempdir( File::Temp->newdir ); $self->tempname( '' . $self->tempdir ); }; after teardown => sub { my $self = shift; $self->tempdir(undef); }; 1; Test-Roo-1.002/t/lib/Skipper.pm000644 000765 000024 00000000202 12220622620 016372 0ustar00davidstaff000000 000000 use 5.008001; package Skipper; use Test::Roo::Role; plan skip_all => "We just want to skip"; test try_me => sub { ok(0) }; 1; Test-Roo-1.002/t/bin/custom-order.pl000644 000765 000024 00000000534 12220622620 017411 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo; use lib 't/lib'; with 'LastTest'; test in_main => sub { pass("main"); }; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/bin/main-order.pl000644 000765 000024 00000000556 12220622620 017027 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo; test first_test => sub { pass("first"); }; test second_test => sub { pass("second"); }; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/bin/not-strict.pl000644 000765 000024 00000000477 12220622620 017102 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo; $foo = "1.23"; test 'just pass' => sub { ok(1) }; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/bin/role-last.pl000644 000765 000024 00000000534 12220622620 016670 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo; use lib 't/lib'; test in_main => sub { pass("main"); }; with 'LastTest'; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/bin/role-not-found.pl000644 000765 000024 00000000651 12220622620 017636 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo; # should import Moo as well use lib 't/lib'; has fixture => ( is => 'ro', default => sub { "hello world" }, ); with qw/RoleNotFoundAnywhere/; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/t/bin/skip-all.pl000644 000765 000024 00000000164 12220622620 016501 0ustar00davidstaff000000 000000 use Test::Roo; plan skip_all => "We just want to skip"; test 'just fail' => sub { ok(0) }; run_me; done_testing; Test-Roo-1.002/t/bin/skip-in-role.pl000644 000765 000024 00000000156 12220622620 017277 0ustar00davidstaff000000 000000 use Test::Roo; use lib 't/lib/'; test 'just fail' => sub { ok(0) }; with 'Skipper'; run_me; done_testing; Test-Roo-1.002/t/bin/unsatisfied.pl000644 000765 000024 00000000467 12220622620 017311 0ustar00davidstaff000000 000000 use 5.008001; use Test::Roo; use lib 't/lib'; with qw/RequiresFixture/; run_me; done_testing; # # This file is part of Test-Roo # # This software is Copyright (c) 2013 by David Golden. # # This is free software, licensed under: # # The Apache License, Version 2.0, January 2004 # # vim: ts=4 sts=4 sw=4 et: Test-Roo-1.002/lib/Test/000755 000765 000024 00000000000 12220622620 015101 5ustar00davidstaff000000 000000 Test-Roo-1.002/lib/Test/Roo/000755 000765 000024 00000000000 12220622620 015640 5ustar00davidstaff000000 000000 Test-Roo-1.002/lib/Test/Roo.pm000644 000765 000024 00000024226 12220622620 016204 0ustar00davidstaff000000 000000 use 5.008001; use strictures; package Test::Roo; # ABSTRACT: Composable, reusable tests with roles and Moo our $VERSION = '1.002'; # VERSION use Test::More 0.96 import => [qw/subtest/]; use Sub::Install; sub import { my ( $class, @args ) = @_; my $caller = caller; for my $sub (qw/test run_me/) { Sub::Install::install_sub( { into => $caller, code => $sub } ); } strictures->import; # do this for Moo, since we load Moo in eval eval qq{ package $caller; use Moo; extends 'Test::Roo::Class' }; if (@args) { eval qq{ package $caller; use Test::More \@args }; } else { eval qq{ package $caller; use Test::More }; } die $@ if $@; } sub test { my ( $name, $code ) = @_; my $caller = caller; my $subtest = sub { my $self = shift; subtest $name => sub { $self->each_test($code) } }; eval qq{ package $caller; after _do_tests => \$subtest }; die $@ if $@; } sub run_me { my $class = caller; $class->run_tests(@_); } 1; # vim: ts=4 sts=4 sw=4 et: __END__ =pod =encoding utf-8 =head1 NAME Test::Roo - Composable, reusable tests with roles and Moo =head1 VERSION version 1.002 =head1 SYNOPSIS Define test behaviors and required fixtures in a role: # t/lib/ObjectCreation.pm package ObjectCreation; use Test::Roo::Role; # loads Moo::Role and Test::More requires 'class'; # we need this fixture test 'object creation' => sub { my $self = shift; require_ok( $self->class ); my $obj = new_ok( $self->class ); }; 1; Provide fixtures and run tests from the .t file: # t/test.t use Test::Roo; # loads Moo and Test::More use lib 't/lib'; # provide the fixture has class => ( is => 'ro', default => sub { "Digest::MD5" }, ); # specify behaviors to test with 'ObjectCreation'; # give our subtests a pretty label sub _build_description { "Testing " . shift->class } # run the test with default fixture run_me; # run the test with different fixture run_me( { class => "Digest::SHA1" } ); done_testing; Result: $ prove -lv t t/test.t .. ok 1 - require Digest::MD5; ok 2 - The object isa Digest::MD5 1..2 ok 1 - object creation 1..1 ok 1 - Testing Digest::MD5 ok 1 - require Digest::SHA1; ok 2 - The object isa Digest::SHA1 1..2 ok 1 - object creation 1..1 ok 2 - Testing Digest::SHA1 1..2 ok All tests successful. Files=1, Tests=2, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.06 cusr 0.00 csys = 0.09 CPU) Result: PASS =head1 DESCRIPTION This module allows you to compose L tests from roles. It is inspired by the excellent L module, but uses L instead of L. This gives most of the benefits without the need for L as a test dependency. Test files are Moo classes. You can define any needed test fixtures as Moo attributes. You define tests as method modifiers -- similar in concept to C in L, but your test method will be passed the test object for access to fixture attributes. You may compose any L into your test to define attributes, require particular methods, or define tests. This means that you can isolate test I into roles which require certain test I in order to run. Your main test file will provide the fixtures and compose the roles to run. This makes it easy to reuse test behaviors. For example, if you are creating tests for Awesome::Module, you could create the test behaviors as Awesome::Module::Test::Role and distribute it with your module. If another distribution subclasses Awesome::Module, it can compose the Awesome::Module::Test::Role behavior for its own tests. No more copying and pasting tests from a super class! Superclasses define and share their tests. Subclasses provide their own fixtures and run the tests. =for Pod::Coverage add_methods_here =head1 USAGE Importing L also loads L (which gives you L with fatal warnings and other goodies) and makes the current package a subclass of L. Importing also loads L. No test plan is used. The C function must be used at the end of every test file. Any import arguments are passed through to Test::More's C method. See also L for test role usage. =head2 Creating fixtures You can create fixtures with normal Moo syntax. You can even make them lazy if you want: has fixture => ( is => 'lazy' ); sub _build_fixture { ... } This becomes really useful with L. A role could define the attribute and require the builder method to be provided by the main test class. =head2 Composing test roles You can use roles to define units of test behavior and then compose them into your test class using the C function. Test roles may define attributes, declare tests, require certain methods and anything else you can regularly do with roles. use Test::Roo; with 'MyTestRole1', 'MyTestRole2'; See L and the L for details and examples. =head2 Setup and teardown You can add method modifiers around the C and C methods and these will be run before tests begin and after tests finish (respectively). before setup => sub { ... }; after teardown => sub { ... }; You can also add method modifiers around C, which will be run before and after B individual test. You could use these to prepare or reset a fixture. has fixture => ( is => 'lazy, clearer => 1, predicate => 1 ); after each_test => sub { shift->clear_fixture }; Roles may also modify C, C, and C, so the order that modifiers will be called will depend on when roles are composed. Be careful with C, though, because the global effect may make composition more fragile. You can call test functions in modifiers. For example, you could confirm that something has been set up or cleaned up. before each_test => sub { ok( ! shift->has_fixture ) }; =head2 Running tests The simplest way to use L with a single F<.t> file is to let the C
package be the test class and call C in it: # t/test.t use Test::Roo; # loads Moo and Test::More has class => ( is => 'ro', default => sub { "Digest::MD5" }, ); test 'load class' => sub { my $self = shift; require_ok( $self->class ); } run_me; done_testing; Calling C<< run_me(@args) >> is equivalent to calling C<< __PACKAGE__->run_tests(@args) >> and runs tests for the current package. You may specify an optional description or hash reference of constructor arguments to customize the test object: run_me( "load MD5" ); run_me( { class => "Digest::MD5" } ); run_me( "load MD5", { class => "Digest::MD5" } ); See L for more about the C method. Alternatively, you can create a separate package (in the test file or in a separate F<.pm> file) and run tests explicitly on that class. # t/test.t package MyTest; use Test::Roo; use lib 't/lib'; has class => ( is => 'ro', required => 1, ); with 'MyTestRole'; package main; use strictures; use Test::More; for my $c ( qw/Digest::MD5 Digest::SHA/ ) { MyTest->run_tests("Testing $c", { class => $c } ); } done_testing; =head1 EXPORTED FUNCTIONS Loading L exports subroutines into the calling package to declare and run tests. =head2 test test $label => sub { ... }; The C function adds a subtest. The code reference will be called with the test object as its only argument. Tests are run in the order declared, so the order of tests from roles will depend on when they are composed relative to other test declarations. =head2 run_me run_me; run_me( $description ); run_me( $init_args ); run_me( $description, $init_args ); The C function calls the C method on the current package and passes all arguments to that method. It takes a description and/or a hash reference of constructor arguments. =head1 DIFFERENCES FROM TEST::ROUTINE While this module was inspired by L, it is not a drop-in replacement. Here is an overview of major differences: =over 4 =item * L uses L; L uses L =item * Loading L makes the importing package a class; in L it becomes a role =item * Loading L loads L; L does not =item * In L, C is a method; in L it is a function and takes arguments in a different order =item * In L, all role composition must be explicit using C; in L, the C command can also compose roles =item * In L, test blocks become method modifiers hooked on an empty method; in L, they become methods run via introspection =item * In L, setup and teardown are done by modifying C and C methods; in L they are done by modifying C =back =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan =head1 SUPPORT =head2 Bugs / Feature Requests Please report any bugs or feature requests through the issue tracker at L. You will be notified automatically of any progress on your issue. =head2 Source Code This is open source software. The code repository is available for public review and contribution under the terms of the license. L git clone https://github.com/dagolden/Test-Roo.git =head1 AUTHOR David Golden =head1 CONTRIBUTOR Diab Jerius =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2013 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 =cut Test-Roo-1.002/lib/Test/Roo/Class.pm000644 000765 000024 00000010137 12220622620 017245 0ustar00davidstaff000000 000000 use 5.008001; use strictures; package Test::Roo::Class; # ABSTRACT: Base class for Test::Roo test classes our $VERSION = '1.002'; # VERSION use Moo; use MooX::Types::MooseLike::Base qw/Str/; use Test::More 0.96 import => [qw/subtest/]; #--------------------------------------------------------------------------# # attributes #--------------------------------------------------------------------------# has description => ( is => 'rw', isa => Str, lazy => 1, builder => 1, ); sub _build_description { my $class = ref $_[0]; return "testing with $class"; } #--------------------------------------------------------------------------# # class or object methods #--------------------------------------------------------------------------# sub run_tests { my $self = shift; # get hashref from end of args # if any args are left, it must be description my ( $desc, $args ); $args = pop if @_ && ref $_[-1] eq 'HASH'; $desc = shift; # create an object if needed and possibly update description $self = $self->new( $args || {} ) if !ref $self; $self->description($desc) if defined $desc; # execute tests wrapped in a subtest subtest $self->description => sub { $self->setup; $self->_do_tests; $self->teardown; }; } #--------------------------------------------------------------------------# # private methods and stubs #--------------------------------------------------------------------------# sub setup { } sub each_test { my ( $self, $code ) = @_; $code->($self); } sub teardown { } # anchor for tests as method modifiers sub _do_tests { } 1; # vim: ts=4 sts=4 sw=4 et: __END__ =pod =encoding utf-8 =head1 NAME Test::Roo::Class - Base class for Test::Roo test classes =head1 VERSION version 1.002 =head1 DESCRIPTION This module is the base class for L test classes. It provides methods to run tests and anchor modifiers. Generally, you should not extend this class yourself, but use L to do so instead. =head1 ATTRIBUTES =head2 description A description for a subtest block wrapping all tests by the object. It is a 'lazy' attribute. Test classes may implement their own C<_build_description> method to create a description from object attributes. Otherwise, the default is "testing with CLASS". =head1 METHODS =head2 run_tests # as a class method $class->run_tests(); $class->run_tests($description); $class->run_tests($init_args); $class->run_tests($description $init_args); # as an object method $self->run_tests(); $self->run_tests($description); If called as a class method, this creates a test object using an optional hash reference of initialization arguments. When called as an object method, or after an object has been generated, this method sets an optional description and runs tests. It will call the C method (triggering any method modifiers), will run all tests (triggering any method modifiers on C) and will call the C method (triggering any method modifiers). If a description is provided, it will override any initialized or generated C attribute. The setup, tests and teardown will be executed in a L subtest block. =head2 setup This is an empty method used to anchor method modifiers. It should not be overridden by subclasses. =head2 each_test This method wraps the code references set by the C function from L or L in a L subtest block. It may also be used to anchor modifiers that should run before or after each test block, though this can lead to brittle design as modifiers will globally affect every test block, including composed ones. =head2 teardown This is an empty method used to anchor method modifiers. It should not be overridden by subclasses. =for Pod::Coverage each_test setup teardown =head1 AUTHOR David Golden =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2013 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 =cut Test-Roo-1.002/lib/Test/Roo/Cookbook.pm000644 000765 000024 00000032234 12220622620 017750 0ustar00davidstaff000000 000000 use 5.008001; use strictures; package Test::Roo::Cookbook; # ABSTRACT: Test::Roo examples our $VERSION = '1.002'; # VERSION 1; # vim: ts=4 sts=4 sw=4 et: __END__ =pod =encoding utf-8 =head1 NAME Test::Roo::Cookbook - Test::Roo examples =head1 VERSION version 1.002 =head1 DESCRIPTION This file offers usage ideas and examples for L. =for Pod::Coverage method_names_here =head1 ORGANIZING TEST CLASSES AND ROLES =head2 Self-contained test file A single test file could be used for simple tests where you want to use Moo attributes for fixtures that get used by test blocks. Here is an example that requires a C attribute, stores lines from that file in the C attribute and makes it available to all test blocks: # examples/cookbook/single_file.t use Test::Roo; use MooX::Types::MooseLike::Base qw/ArrayRef/; use Path::Tiny; has corpus => ( is => 'ro', isa => sub { -f shift }, required => 1, ); has lines => ( is => 'lazy', isa => ArrayRef, ); sub _build_lines { my ($self) = @_; return [ map { lc } path( $self->corpus )->lines ]; } test 'sorted' => sub { my $self = shift; is_deeply( $self->lines, [ sort @{$self->lines} ], "alphabetized"); }; test 'a to z' => sub { my $self = shift; my %letters = map { substr($_,0,1) => 1 } @{ $self->lines }; is_deeply( [sort keys %letters], ["a" .. "z"], "all letters found" ); }; run_me( { corpus => "/usr/share/dict/words" } ); # ... test other corpuses ... done_testing; =head2 Standalone test class You don't have to put the test class into the F<.t> file. It's just a class. Here is the same corpus checking example as before, but now as a class: # examples/cookbook/lib/CorpusCheck.pm package CorpusCheck; use Test::Roo; use MooX::Types::MooseLike::Base qw/ArrayRef/; use Path::Tiny; has corpus => ( is => 'ro', isa => sub { -f shift }, required => 1, ); has lines => ( is => 'lazy', isa => ArrayRef, ); sub _build_lines { my ($self) = @_; return [ map { lc } path( $self->corpus )->lines ]; } test 'sorted' => sub { my $self = shift; is_deeply( $self->lines, [ sort @{$self->lines} ], "alphabetized"); }; test 'a to z' => sub { my $self = shift; my %letters = map { substr($_,0,1) => 1 } @{ $self->lines }; is_deeply( [sort keys %letters], ["a" .. "z"], "all letters found" ); }; 1; Running it from a F<.t> file doesn't even need L: # examples/cookbook/standalone.t use strictures; use Test::More; use lib 'lib'; use CorpusCheck; CorpusCheck->run_tests({ corpus => "/usr/share/dict/words" }); done_testing; =head2 Standalone Test Roles The real power of L is decomposing test behaviors into roles that can be reused. Imagine we want to test a file-finder module like L. We could put tests for it into a role, then run the tests from a file that composes that role. For example, here would be the test file: # examples/cookbook/test-pir.pl use Test::Roo; use lib 'lib'; with 'IteratorTest'; run_me( { iterator_class => 'Path::Iterator::Rule', result_type => '', } ); done_testing; Then in the distribution for L, the same role could be tested with a test file like this: # examples/cookbook/test-pcr.pl use Test::Roo; use lib 'lib'; with 'IteratorTest'; run_me( { iterator_class => 'Path::Class::Rule', result_type => 'Path::Class::Entity', }, ); done_testing; What is the common role that they are consuming? It sets up a test directory, creates files and runs tests: # examples/cookbook/lib/IteratorTest.pm package IteratorTest; use Test::Roo::Role; use MooX::Types::MooseLike::Base qw/:all/; use Class::Load qw/load_class/; use Path::Tiny; has [qw/iterator_class result_type/] => ( is => 'ro', isa => Str, required => 1, ); has test_files => ( is => 'ro', isa => ArrayRef, default => sub { return [ qw( aaaa bbbb cccc/dddd eeee/ffff/gggg ) ]; }, ); has tempdir => ( is => 'lazy', isa => InstanceOf ['Path::Tiny'] ); has rule_object => ( is => 'lazy', isa => Object, clearer => 1, ); sub _build_description { return shift->iterator_class } sub _build_tempdir { my ($self) = @_; my $dir = Path::Tiny->tempdir; $dir->child($_)->touchpath for @{ $self->test_files }; return $dir; } sub _build_rule_object { my ($self) = @_; load_class( $self->iterator_class ); return $self->iterator_class->new; } sub test_result_type { my ( $self, $file ) = @_; if ( my $type = $self->result_type ) { isa_ok( $file, $type, $file ); } else { is( ref($file), '', "$file is string" ); } } test 'find files' => sub { my $self = shift; $self->clear_rule_object; # make sure have a new one each time $self->tempdir; my $rule = $self->rule_object; my @files = $rule->file->all( $self->tempdir, { relative => 1 } ); is_deeply( \@files, $self->test_files, "correct list of files" ) or diag explain \@files; $self->test_result_type($_) for @files; }; # ... more tests ... 1; =head1 CREATING AND MANAGING FIXTURES =head2 Skipping all tests If you need to skip all tests in the F<.t> file because some prerequisite isn't available or some fixture couldn't be built, use a C method and call C<< plan skip_all => $reason >>. use Class::Load qw/try_load_class/; has fixture => ( is => 'lazy', ); sub _build_fixture { # ... something that might die if unavailable ... } sub BUILD { my ($self) = @_; try_load_class('Class::Name') or plan skip_all => "Class::Name required to run these tests"; eval { $self->fixture } or plan skip_all => "Couldn't build fixture"; } =head2 Setting a test description You can override C<_build_description> to create a test description based on other attributes. For example, the C package earlier had these lines: has [qw/iterator_class result_type/] => ( is => 'ro', isa => Str, required => 1, ); sub _build_description { return shift->iterator_class } The C attribute is required and then the description is set to it. Or, there could be a more verbose description: sub _build_description { my $name = shift->iterator_class; return "Testing the $name class" } =head2 Requiring a builder A test role can specify a lazy attribute and then require the consuming class to provide a builder for it. In the test role: has fixture => ( is => 'lazy', ); requires '_build_fixture'; In the consuming class: sub _build_fixture { ... } =head2 Clearing fixtures If a fixture has a clearer method, it can be easily reset during testing. This works really well with lazy attributes which get regenerated on demand. has fixture => ( is => 'lazy', clearer => 1, ); test "some test" => sub { my $self = shift; $self->clear_fixture; ... }; =head1 MODIFIERS FOR SETUP AND TEARDOWN =head2 Setting up a fixture before testing When you need to do some extra work to set up a fixture, you can put a method modifier on the C method. In some cases, this is more intuitive than doing all the work in an attribute builder. Here is an example that creates an SQLite table before any tests are run and cleans up afterwards: # example/cookbook/sqlite.t use Test::Roo; use DBI; use Path::Tiny; has tempdir => ( is => 'ro', clearer => 1, default => sub { Path::Tiny->tempdir }, ); has dbfile => ( is => 'lazy', default => sub { shift->tempdir->child('test.sqlite3') }, ); has dbh => ( is => 'lazy', ); sub _build_dbh { my $self = shift; DBI->connect( "dbi:SQLite:dbname=" . $self->dbfile, { RaiseError => 1 } ); } before 'setup' => sub { my $self = shift; $self->dbh->do("CREATE TABLE f (f1, f2, f3)"); }; after 'teardown' => sub { shift->clear_tempdir }; test 'first' => sub { my $self = shift; my $dbh = $self->dbh; my $sth = $dbh->prepare("INSERT INTO f(f1,f2,f3) VALUES (?,?,?)"); ok( $sth->execute( "one", "two", "three" ), "inserted data" ); my $got = $dbh->selectrow_arrayref("SELECT * FROM f"); is_deeply( $got, [qw/one two three/], "read data" ); }; run_me; done_testing; =head2 Running tests during setup and teardown You can run any tests you like during setup or teardown. The previous example could have written the setup and teardown hooks like this: before 'setup' => sub { my $self = shift; ok( ! -f $self->dbfile, "test database file not created" ); ok( $self->dbh->do("CREATE TABLE f (f1, f2, f3)"), "created table"); ok( -f $self->dbfile, "test database file exists" ); }; after 'teardown' => sub { my $self = shift; my $dir = $self->tempdir; $self->clear_tempdir; ok( ! -f $dir, "tempdir cleaned up"); }; =head1 MODIFIERS ON TESTS =head2 Global modifiers with C Modifying C triggers methods before or after B test block defined with the C function. Because this affects all tests, whether from the test class or composed from roles, it needs to be used thoughtfully. Here is an example that ensures that every test block is run in its own separate temporary directory. # examples/cookbook/with_tempd.t use Test::Roo; use File::pushd qw/tempd/; use Cwd qw/getcwd/; has tempdir => ( is => 'lazy', isa => sub { shift->isa('File::pushd') }, clearer => 1, ); # tempd changes directory until the object is destroyed # and the fixture caches the object until cleared sub _build_tempdir { return tempd() } # building attribute will change to temp directory before each_test => sub { shift->tempdir }; # clearing attribute will change to original directory after each_test => sub { shift->clear_tempdir }; # do stuff in a temp directory test 'first test' => sub { my $self = shift; is( $self->tempdir, getcwd(), "cwd is " . $self->tempdir ); # ... more tests ... }; # do stuff in a separate, fresh temp directory test 'second test' => sub { my $self = shift; is( $self->tempdir, getcwd(), "cwd is " . $self->tempdir ); # ... more tests ... }; run_me; done_testing; =head2 Individual test modifiers If you want to have method modifiers on an individual test, put your L tests in a method, add modifiers to that method, and use C to invoke it. # examples/cookbook/hookable_test.t use Test::Roo; has counter => ( is => 'rw', default => sub { 0 } ); sub is_positive { my $self = shift; ok( $self->counter > 0, "counter is positive" ); } before is_positive => sub { shift->counter( 1 ) }; test 'hookable' => sub { shift->is_positive }; run_me; done_testing; =head2 Wrapping tests As a middle ground between global and individual modifiers, if you need to call some code repeatedly for some, but not all all tests, you can create a custom test function. This might make sense for only a few tests, but could be helpful if there are many that need similar behavior, but you can't make it global by modifying C. The following example clears the fixture before tests defined with the C function. # examples/cookbook/wrapped.t use strict; use Test::Roo; has fixture => ( is => 'rw', lazy => 1, builder => 1, clearer => 1, ); sub _build_fixture { "Hello World" } sub fresh_test { my ($name, $code) = @_; test $name, sub { my $self = shift; $self->clear_fixture; $code->($self); }; } fresh_test 'first' => sub { my $self = shift; is ( $self->fixture, 'Hello World', "fixture has default" ); $self->fixture("Goodbye World"); }; fresh_test 'second' => sub { my $self = shift; is ( $self->fixture, 'Hello World', "fixture has default" ); }; run_me; done_testing; =head1 AUTHOR David Golden =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2013 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 =cut Test-Roo-1.002/lib/Test/Roo/Role.pm000644 000765 000024 00000006770 12220622620 017111 0ustar00davidstaff000000 000000 use 5.008001; use strictures; package Test::Roo::Role; # ABSTRACT: Composable role for Test::Roo our $VERSION = '1.002'; # VERSION use Test::Roo (); # no imports! use Sub::Install; sub import { my ( $class, @args ) = @_; my $caller = caller; Sub::Install::install_sub( { into => $caller, code => 'test', from => 'Test::Roo' } ); strictures->import; # do this for Moo, since we load Moo in eval eval qq{ package $caller; use Moo::Role; }; if (@args) { eval qq{ package $caller; use Test::More \@args }; } else { eval qq{ package $caller; use Test::More }; } die $@ if $@; } 1; # vim: ts=4 sts=4 sw=4 et: __END__ =pod =encoding utf-8 =head1 NAME Test::Roo::Role - Composable role for Test::Roo =head1 VERSION version 1.002 =head1 SYNOPSIS A testing role: # t/lib/MyTestRole.pm package MyTestRole; use Test::Roo::Role; # loads Moo::Role and Test::More requires 'class'; test 'object creation' => sub { my $self = shift; require_ok( $self->class ); my $obj = new_ok( $self->class ); }; 1; =head1 DESCRIPTION This module defines test behaviors as a L. =for Pod::Coverage method_names_here =head1 USAGE Importing L also loads L (which gives you L with fatal warnings and other goodies). Importing also loads L. Any import arguments are passed through to Test::More's C method. =head2 Creating and requiring fixtures You can create fixtures with normal Moo syntax. You can even make them lazy if you want and require the composing class to provide the builder: has fixture => ( is => 'lazy' ); requires '_build_fixture'; Because this is a L, you can require any method you like, not just builders. See L and L for everything you can do with roles. =head2 Setup and teardown You can add method modifiers around the C and C methods and these will be run before tests begin and after tests finish (respectively). before setup => sub { ... }; after teardown => sub { ... }; You can also add method modifiers around C, which will be run before and after B individual test. You could use these to prepare or reset a fixture. has fixture => ( is => 'lazy, clearer => 1, predicate => 1 ); after each_test => sub { shift->clear_fixture }; Roles may also modify C, C, and C, so the order that modifiers will be called will depend on when roles are composed. Be careful with C, though, because the global effect may make composition more fragile. You can call test functions in modifiers. For example, you could confirm that something has been set up or cleaned up. before each_test => sub { ok( ! shift->has_fixture ) }; =head1 EXPORTED FUNCTIONS Loading L exports a single subroutine into the calling package to declare tests. =head2 test test $label => sub { ... }; The C function adds a subtest. The code reference will be called with the test object as its only argument. Tests are run in the order declared, so the order of tests from roles will depend on when they are composed relative to other test declarations. =head1 AUTHOR David Golden =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2013 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 =cut Test-Roo-1.002/examples/cookbook/000755 000765 000024 00000000000 12220622620 017040 5ustar00davidstaff000000 000000 Test-Roo-1.002/examples/synopsis/000755 000765 000024 00000000000 12220622620 017121 5ustar00davidstaff000000 000000 Test-Roo-1.002/examples/synopsis/README000644 000765 000024 00000000130 12220622620 017773 0ustar00davidstaff000000 000000 SYNOPIS example Assuming Test::Roo is installed, rom this directory: $ prove -v t Test-Roo-1.002/examples/synopsis/t/000755 000765 000024 00000000000 12220622620 017364 5ustar00davidstaff000000 000000 Test-Roo-1.002/examples/synopsis/t/lib/000755 000765 000024 00000000000 12220622620 020132 5ustar00davidstaff000000 000000 Test-Roo-1.002/examples/synopsis/t/test.t000644 000765 000024 00000000565 12220622620 020536 0ustar00davidstaff000000 000000 use Test::Roo; # loads Moo and Test::More use lib 't/lib'; # provide the fixture has class => ( is => 'ro', default => sub { "Digest::MD5" }, ); # specify behaviors to test with 'ObjectCreation'; # give our subtests a label sub _build_description { "Testing " . shift->class } # run the tests run_me; run_me( { class => "Digest::SHA1" } ); done_testing; Test-Roo-1.002/examples/synopsis/t/lib/ObjectCreation.pm000644 000765 000024 00000000406 12220622620 023363 0ustar00davidstaff000000 000000 package ObjectCreation; use Test::Roo::Role; # loads Moo::Role and Test::More requires 'class'; # we need this fixture test 'object creation' => sub { my $self = shift; require_ok( $self->class ); my $obj = new_ok( $self->class ); }; 1; Test-Roo-1.002/examples/cookbook/hookable_test.t000644 000765 000024 00000000440 12220622620 022046 0ustar00davidstaff000000 000000 use Test::Roo; has counter => ( is => 'rw', default => sub { 0 } ); sub is_positive { my $self = shift; ok( $self->counter > 0, "counter is positive" ); } before is_positive => sub { shift->counter( 1 ) }; test 'hookable' => sub { shift->is_positive }; run_me; done_testing; Test-Roo-1.002/examples/cookbook/lib/000755 000765 000024 00000000000 12220622620 017606 5ustar00davidstaff000000 000000 Test-Roo-1.002/examples/cookbook/single_file.t000644 000765 000024 00000001341 12220622620 021504 0ustar00davidstaff000000 000000 use Test::Roo; use MooX::Types::MooseLike::Base qw/ArrayRef/; use Path::Tiny; has corpus => ( is => 'ro', isa => sub { -f shift }, required => 1, ); has lines => ( is => 'lazy', isa => ArrayRef, ); sub _build_lines { my ($self) = @_; return [ map { lc } path( $self->corpus )->lines ]; } test 'sorted' => sub { my $self = shift; is_deeply( $self->lines, [ sort @{$self->lines} ], "alphabetized"); }; test 'a to z' => sub { my $self = shift; my %letters = map { substr($_,0,1) => 1 } @{ $self->lines }; is_deeply( [sort keys %letters], ["a" .. "z"], "all letters found" ); }; run_me( { corpus => "/usr/share/dict/words" } ); # ... test other corpuses ... done_testing; Test-Roo-1.002/examples/cookbook/sqlite-confirm.t000644 000765 000024 00000002211 12220622620 022155 0ustar00davidstaff000000 000000 use Test::Roo; use DBI; use Path::Tiny; has tempdir => ( is => 'ro', clearer => 1, default => sub { Path::Tiny->tempdir }, ); has dbfile => ( is => 'lazy', default => sub { shift->tempdir->child('test.sqlite3') }, ); has dbh => ( is => 'lazy', ); sub _build_dbh { my $self = shift; DBI->connect( "dbi:SQLite:dbname=" . $self->dbfile, { RaiseError => 1 } ); } before 'setup' => sub { my $self = shift; ok( ! -f $self->dbfile, "test database file not created" ); ok( $self->dbh->do("CREATE TABLE f (f1, f2, f3)"), "created table"); ok( -f $self->dbfile, "test database file exists" ); }; after 'teardown' => sub { my $self = shift; my $dir = $self->tempdir; $self->clear_tempdir; ok( ! -f $dir, "tempdir cleaned up"); }; test 'first' => sub { my $self = shift; my $dbh = $self->dbh; my $sth = $dbh->prepare("INSERT INTO f(f1,f2,f3) VALUES (?,?,?)"); ok( $sth->execute( "one", "two", "three" ), "inserted data" ); my $got = $dbh->selectrow_arrayref("SELECT * FROM f"); is_deeply( $got, [qw/one two three/], "read data" ); }; run_me; done_testing; Test-Roo-1.002/examples/cookbook/sqlite.t000644 000765 000024 00000001627 12220622620 020534 0ustar00davidstaff000000 000000 use Test::Roo; use DBI; use Path::Tiny; has tempdir => ( is => 'ro', clearer => 1, default => sub { Path::Tiny->tempdir }, ); has dbfile => ( is => 'lazy', default => sub { shift->tempdir->child('test.sqlite3') }, ); has dbh => ( is => 'lazy', ); sub _build_dbh { my $self = shift; DBI->connect( "dbi:SQLite:dbname=" . $self->dbfile, { RaiseError => 1 } ); } before 'setup' => sub { my $self = shift; $self->dbh->do("CREATE TABLE f (f1, f2, f3)"); }; after 'teardown' => sub { shift->clear_tempdir }; test 'first' => sub { my $self = shift; my $dbh = $self->dbh; my $sth = $dbh->prepare("INSERT INTO f(f1,f2,f3) VALUES (?,?,?)"); ok( $sth->execute( "one", "two", "three" ), "inserted data" ); my $got = $dbh->selectrow_arrayref("SELECT * FROM f"); is_deeply( $got, [qw/one two three/], "read data" ); }; run_me; done_testing; Test-Roo-1.002/examples/cookbook/standalone.t000644 000765 000024 00000000220 12220622620 021347 0ustar00davidstaff000000 000000 use strictures; use Test::More; use lib 'lib'; use CorpusCheck; CorpusCheck->run_tests({ corpus => "/usr/share/dict/words" }); done_testing; Test-Roo-1.002/examples/cookbook/test-pcr.pl000644 000765 000024 00000000275 12220622620 021142 0ustar00davidstaff000000 000000 use Test::Roo; use lib 'lib'; with 'IteratorTest'; run_me( { iterator_class => 'Path::Class::Rule', result_type => 'Path::Class::Entity', }, ); done_testing; Test-Roo-1.002/examples/cookbook/test-pir.pl000644 000765 000024 00000000254 12220622620 021145 0ustar00davidstaff000000 000000 use Test::Roo; use lib 'lib'; with 'IteratorTest'; run_me( { iterator_class => 'Path::Iterator::Rule', result_type => '', } ); done_testing; Test-Roo-1.002/examples/cookbook/with_tempd.t000644 000765 000024 00000001601 12220622620 021367 0ustar00davidstaff000000 000000 use Test::Roo; use File::pushd qw/tempd/; use Cwd qw/getcwd/; has tempdir => ( is => 'lazy', isa => sub { shift->isa('File::pushd') }, clearer => 1, ); # tempd changes directory until the object is destroyed # and the fixture caches the object until cleared sub _build_tempdir { return tempd() } # building attribute will change to temp directory before each_test => sub { shift->tempdir }; # clearing attribute will change to original directory after each_test => sub { shift->clear_tempdir }; # do stuff in a temp directory test 'first test' => sub { my $self = shift; is( $self->tempdir, getcwd(), "cwd is " . $self->tempdir ); # ... more tests ... }; # do stuff in a separate, fresh temp directory test 'second test' => sub { my $self = shift; is( $self->tempdir, getcwd(), "cwd is " . $self->tempdir ); # ... more tests ... }; run_me; done_testing; Test-Roo-1.002/examples/cookbook/wrapped.t000644 000765 000024 00000001141 12220622620 020664 0ustar00davidstaff000000 000000 use strict; use Test::Roo; has fixture => ( is => 'rw', lazy => 1, builder => 1, clearer => 1, ); sub _build_fixture { "Hello World" } sub fresh_test { my ($name, $code) = @_; test $name, sub { my $self = shift; $self->clear_fixture; $code->($self); }; } fresh_test 'first' => sub { my $self = shift; is ( $self->fixture, 'Hello World', "fixture has default" ); $self->fixture("Goodbye World"); }; fresh_test 'second' => sub { my $self = shift; is ( $self->fixture, 'Hello World', "fixture has default" ); }; run_me; done_testing; Test-Roo-1.002/examples/cookbook/lib/CorpusCheck.pm000644 000765 000024 00000001232 12220622620 022353 0ustar00davidstaff000000 000000 package CorpusCheck; use Test::Roo; use MooX::Types::MooseLike::Base qw/ArrayRef/; use Path::Tiny; has corpus => ( is => 'ro', isa => sub { -f shift }, required => 1, ); has lines => ( is => 'lazy', isa => ArrayRef, ); sub _build_lines { my ($self) = @_; return [ map { lc } path( $self->corpus )->lines ]; } test 'sorted' => sub { my $self = shift; is_deeply( $self->lines, [ sort @{$self->lines} ], "alphabetized"); }; test 'a to z' => sub { my $self = shift; my %letters = map { substr($_,0,1) => 1 } @{ $self->lines }; is_deeply( [sort keys %letters], ["a" .. "z"], "all letters found" ); }; 1; Test-Roo-1.002/examples/cookbook/lib/IteratorTest.pm000644 000765 000024 00000003135 12220622620 022577 0ustar00davidstaff000000 000000 package IteratorTest; use Test::Roo::Role; use MooX::Types::MooseLike::Base qw/:all/; use Class::Load qw/load_class/; use Path::Tiny; has [qw/iterator_class result_type/] => ( is => 'ro', isa => Str, required => 1, ); has test_files => ( is => 'ro', isa => ArrayRef, default => sub { return [ qw( aaaa bbbb cccc/dddd eeee/ffff/gggg ) ]; }, ); has tempdir => ( is => 'lazy', isa => InstanceOf ['Path::Tiny'] ); has rule_object => ( is => 'lazy', isa => Object, clearer => 1, ); sub _build_description { return shift->iterator_class } sub _build_tempdir { my ($self) = @_; my $dir = Path::Tiny->tempdir; $dir->child($_)->touchpath for @{ $self->test_files }; return $dir; } sub _build_rule_object { my ($self) = @_; load_class( $self->iterator_class ); return $self->iterator_class->new; } sub test_result_type { my ( $self, $file ) = @_; if ( my $type = $self->result_type ) { isa_ok( $file, $type, $file ); } else { is( ref($file), '', "$file is string" ); } } test 'find files' => sub { my $self = shift; $self->clear_rule_object; # make sure have a new one each time $self->tempdir; my $rule = $self->rule_object; my @files = $rule->file->all( $self->tempdir, { relative => 1 } ); is_deeply( \@files, $self->test_files, "correct list of files" ) or diag explain \@files; $self->test_result_type($_) for @files; }; # ... more tests ... 1;