Perl日記

PerlとかRubyとかPHPとかPythonとか

Test::Specその3

they

xdescribe/xcontext/xit/xthey

  • それぞれdescribe/context/itの代わりに使う
  • 要はpending扱いにしてくれる
  • TODOラベルを貼る必要がない

spec_helper

  • 各テストファイルで共通のものを書いておいて、spec_helperでそのファイルを各テストファイルから呼び出す
  • shared_examples_for も置ける
  • ただし変数スコープは存在するので、共有にはshareを使わないといけない
  • use strict/warningsしなくても、その恩恵には与れる

share

  • 同じテストファイル内で変数を受け渡すなら、そのファイル内のトップレベルに変数を宣言すればいいけれど、各テストで共有したいときにshareを使う
  • spec_helper.plに使うのが便利そう
  • 使用範囲がそのブロックに収束するなら、myも一緒に宣言
  • 変数名は別になんでもいい
  • 要するにtie
  • っていうか実装もtieされてるだけだった


その2までやってた分をspec_helper.plに分けてみた。


lib/MessageFilter.pm

package MessageFilter {
  use strict;
  use warnings;

  sub new {
    my ($class, @ng_words) = @_;
    bless { ng_words => \@ng_words }, $class;
  }

  sub ng_words {
    my $self = shift;
    return @{$self->{ng_words}};
  }

  sub is_detect {
    my ($self, $word) = @_;
    return grep index($word, $_) >= 0, $self->ng_words;
  }
}

1;

t/spec_helper.pl

# use warningsされるかテスト
undef == 1;

shared_examples_for 'MessageFilter with argument "foo"' => sub {                                                        
  share my %vars; # shareで共有

  it 'should detect message with NG word' => sub {
    ok($vars{filter}->is_detect('hello from foo'));
  };
  it 'should not detect message without NG word' => sub {
    ok(!$vars{filter}->is_detect('hello, world'));
  };
  it 'ng_words should not be empty' => sub {
    ok($vars{filter}->ng_words);
  };
};

t/message_filter_spec.t

use MessageFilter;
use Test::Spec;                                                                                                         

# 外部ファイル読み込み
spec_helper 'spec_helper.pl';

describe MessageFilter => sub {
  share my %vars; # shareで共有

  my $filter;

  context 'with argument "foo"' => sub {
    before sub {
      # share変数にも入れておく
      $vars{filter} = $filter = MessageFilter->new('foo');
    };

    it_should_behave_like 'MessageFilter with argument "foo"';
    it 'ng_words size is 1' => sub {
      cmp_ok(scalar($filter->ng_words), '==', 1);
    };
  };

  context 'with argument "foo","bar"' => sub {
    before sub {
      # share変数にも入れておく
      $vars{filter} = $filter = MessageFilter->new('foo', 'bar');
    };

    it 'should detect message with NG word' => sub {
      ok($filter->is_detect('hello from bar'));
    };
    it_should_behave_like 'MessageFilter with argument "foo"';
    it 'ng_words size is 2' => sub {
      cmp_ok(scalar($filter->ng_words), '==', 2);
    };
  };

  # pending
  xcontext 'with argument qr/foo/' => sub {
    before sub {
      $filter = MessageFilter->new(qr/foo/);
    };
    xit 'should detect message with NG word' => sub {
      ok($filter->is_detect('hello from foo'));
    };
  };
};

runtests unless caller;
$ perl -Ilib t/message_filter_spec.t 
Useless use of numeric eq (==) in void context at spec_helper.pl line 1, <> line 1.
Use of uninitialized value in numeric eq (==) at spec_helper.pl line 1, <> line 1.
ok 1 - MessageFilter with argument "foo" ng_words size is 1
ok 2 - MessageFilter with argument "foo" should detect message with NG word
ok 3 - MessageFilter with argument "foo" should not detect message without NG word
ok 4 - MessageFilter with argument "foo" ng_words should not be empty
ok 5 - MessageFilter with argument "foo","bar" should detect message with NG word
ok 6 - MessageFilter with argument "foo","bar" ng_words size is 2
ok 7 - MessageFilter with argument "foo","bar" should detect message with NG word
ok 8 - MessageFilter with argument "foo","bar" should not detect message without NG word
ok 9 - MessageFilter with argument "foo","bar" ng_words should not be empty
ok 10 - MessageFilter with argument qr/foo/ should detect message with NG word # TODO (disabled)
1..10

Test::Spec::Mocks

  • RubyのMochaからインスパイアされたものらしい
# インスタンスメソッド
# doメソッドを呼び出されたら、無条件で1を返す
$dbh->stubs(do => sub { $sql = shift; return 1 });
$dbh->do('SET NAMES utf8'); #=> 1

# クラスメソッドでもOK
DateTime->stubs('now' => sub { DateTime->new(from_epoch => 0xdeafcab) });