package CMMITTests; use strict; use warnings; use constant CMMIT => qw(Class::Mock::Method::InterfaceTester); use base qw(CMTests::TestClass); use CMMITTestClass; use CMMITTestClass::Subclass; # very similar pre-amble to class-mock-generic-interfacetester.t # how about some Test::Class and inheritance? use Config; use Test::More; use Scalar::Util qw(blessed); use Capture::Tiny qw(capture); use Class::Mock::Method::InterfaceTester; # mock _ok in C::M::M::IT sub _setup_mock :Test(setup) { Class::Mock::Method::InterfaceTester->_ok(sub { my($result, $message) = @_; return ($result ? '' : 'not ')."ok 94 $message"; }); } sub default_ok :Tests(1) { my $perl = $Config{perlpath}; my $result; { local $ENV{PERL5LIB} = join($Config{path_sep}, @INC); $result = (capture { system( $perl, qw( -MClass::Mock::Method::InterfaceTester -e ), " Class::Mock::Method::InterfaceTester->new([ { input => ['wobble'], output => 'jelly' } ]) " ) })[0]; } ok($result =~ /^not ok.*didn't run all tests/, "normal 'ok' works") || diag($result); } sub _check_result { my($expected, $got, $message) = @_; $expected =~ s/%s/.*?/g; $expected = qr/$expected/s; like($got, $expected, $message); } sub wrong_args_structure :Tests(1) { CMMITTestClass->_reset_test_method(); CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { input => ['foo'], output => 'foo' }, ]) ); _check_result( CMMIT->WRONG_ARGS_W_EXPECTED, CMMITTestClass->_test_method('bar'), "detects wrong args to method" ); } sub wrong_args_subref :Tests(2) { CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { input => sub { $_[0] eq 'foo' } , output => 'foo' }, { input => sub { $_[0] eq 'foo' } , output => 'foo' }, ]) ); ok(CMMITTestClass->_test_method('foo') eq 'foo', "correct method call gets right result back (checking with a subref)"); _check_result( CMMIT->WRONG_ARGS, CMMITTestClass->_test_method('bar'), "detects wrong args to method (checking with a subref)" ); } sub correct_method_call_gets_correct_results :Tests(2) { CMMITTestClass->_reset_test_method(); ok(CMMITTestClass->_test_method('foo') eq "called test_method on CMMITTestClass with [foo]\n", "calling a method after _reset()ing works" ); CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { input => ['foo'], output => 'foo' }, ]) ); ok(CMMITTestClass->_test_method('foo') eq 'foo', "correct method call gets right result back"); } sub run_out_of_tests :Tests(1) { CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { input => ['foo'], output => 'foo' }, ]) ); CMMITTestClass->_test_method('foo'); # eat the first test _check_result( CMMIT->RUN_OUT, CMMITTestClass->_test_method('bar'), "run out of tests" ); } sub didnt_run_all_tests :Tests(1) { CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { input => ['foo'], output => 'foo' }, ]) ); # the DESTROY spits out a test, so we need to do this # because we can't capture its return value Class::Mock::Method::InterfaceTester->_ok(sub { Test::More::ok(!shift(), shift()); }); CMMITTestClass->_reset_test_method(); } sub inheritance :Tests(3) { CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { input => ['foo'], output => 'foo' }, ]) ); ok(CMMITTestClass::Subclass->test_method('foo') eq "called test_method on CMMITTestClass::Subclass with [foo]\n", "yup, subclass is good (sanity check)"); ok(CMMITTestClass::Subclass->_test_method('foo') eq 'foo', "called mock on subclass OK"); _check_result( CMMIT->RUN_OUT, CMMITTestClass::Subclass->_test_method('foo'), "run out of tests (using inheritance)" ); } sub invocant_class_and_object :Tests(1) { CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { invocant_class => 'CMMITTestClass', invocant_object => 'CMMITTestClass', input => ['foo'], output => 'foo' }, ]) ); _check_result( CMMIT->BOTH_INVOCANTS, CMMITTestClass->_test_method('foo'), "can't have both of _invocant_{class,object}" ); } sub invocant_class :Tests(5) { CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { invocant_class => 'CMMITTestClass', input => ['foo'], output => 'foo' }, { invocant_class => 'CMMITTestClass', input => ['foo'], output => 'foo' }, { invocant_class => 'CMMITTestClass', input => ['foo'], output => 'foo' }, { invocant_class => 'CMMITTestClass::Subclass', input => ['foo'], output => 'foo' }, { invocant_class => 'CMMITTestClass::Subclass', input => ['foo'], output => 'foo' }, ]) ); _check_result( CMMIT->EXP_CLASS_GOT_OBJECT, bless({}, 'CMMITTestClass')->_test_method('foo'), "called method on object, not class" ); ok(CMMITTestClass->_test_method('foo') eq 'foo', "called on right class"); _check_result( CMMIT->WRONG_CLASS, CMMITTestClass::Subclass->_test_method('foo'), "called on wrong class, via inheritance" ); _check_result( CMMIT->WRONG_CLASS, CMMITTestClass->_test_method('foo'), "called on wrong class" ); ok(CMMITTestClass::Subclass->_test_method('foo') eq 'foo', "called on right class via inheritance"); } # re-factor these two sub invocant_object_string :Tests(5) { CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { invocant_object => 'CMMITTestClass', input => ['foo'], output => 'foo' }, { invocant_object => 'CMMITTestClass', input => ['foo'], output => 'foo' }, { invocant_object => 'CMMITTestClass', input => ['foo'], output => 'foo' }, { invocant_object => 'CMMITTestClass::Subclass', input => ['foo'], output => 'foo' }, { invocant_object => 'CMMITTestClass::Subclass', input => ['foo'], output => 'foo' }, ]) ); _check_result( CMMIT->EXP_OBJECT_GOT_CLASS, CMMITTestClass->_test_method('foo'), "called method on class, not object" ); ok(bless({}, 'CMMITTestClass')->_test_method('foo') eq 'foo', "called on object of right class"); _check_result( CMMIT->WRONG_OBJECT, bless({}, 'CMMITTestClass::Subclass')->_test_method('foo'), "called method on object of wrong class, via inheritance" ); _check_result( CMMIT->WRONG_OBJECT, bless({}, 'CMMITTestClass')->_test_method('foo'), "called method on object of wrong class" ); ok(bless({}, 'CMMITTestClass::Subclass')->_test_method('foo') eq 'foo', "called on object of right class via inheritance"); } sub invocant_object_subref :Tests(4) { CMMITTestClass->_set_test_method( Class::Mock::Method::InterfaceTester->new([ { invocant_object => sub { blessed($_[0]) eq 'CMMITTestClass' }, input => ['foo'], output => 'foo' }, { invocant_object => sub { blessed($_[0]) eq 'CMMITTestClass' }, input => ['foo'], output => 'foo' }, { invocant_object => sub { blessed($_[0]) eq 'CMMITTestClass::Subclass' }, input => ['foo'], output => 'foo' }, { invocant_object => sub { blessed($_[0]) eq 'CMMITTestClass::Subclass' }, input => ['foo'], output => 'foo' }, ]) ); ok(bless({}, 'CMMITTestClass')->_test_method('foo') eq 'foo', "called on object that matches sub-ref"); _check_result( CMMIT->WRONG_OBJECT_SUBREF, bless({}, 'CMMITTestClass::Subclass')->_test_method('foo'), "called on object that doesn't match sub-ref, via inheritance" ); _check_result( CMMIT->WRONG_OBJECT_SUBREF, bless({}, 'CMMITTestClass')->_test_method('foo'), "called on object that doesn't match sub-ref" ); ok(bless({}, 'CMMITTestClass::Subclass')->_test_method('foo') eq 'foo', "called on object that matches sub-ref, via inheritance"); } 1;