我们如何对 Mojolicious 控制器进行单元测试?

发布于 2024-10-28 05:35:01 字数 2680 浏览 6 评论 0原文

我们创建了以下简单的 Mojolicious 控制器:

package SampleApp::Pages;

# $Id$

use strict;
use warnings;

our $VERSION = '0.01';

use Mojo::Base 'Mojolicious::Controller';

sub home {
    my $self = shift;

    $self->render( 'title' => 'Home' );

    return;
}

sub contact {
    my $self = shift;

    $self->render( 'title' => 'Contact' );

    return;
}

sub about {
    my $self = shift;

    $self->render( 'title' => 'About' );

    return;
}

1;

相应的单元测试如下所示:

package Test::SampleApp::Pages;

# $Id$

use strict;
use warnings;

our $VERSION = '0.01';

use Carp;
use English '-no_match_vars';
use Readonly;
use Test::Mojo;
use Test::Most;

use base 'Test::Class';

Readonly my $SERVER_OK => 200;

sub startup : Tests(startup) {
    eval {
        require SampleApp;

        SampleApp->import;

        1;
    } or Carp::croak($EVAL_ERROR);

    return;
}

sub get_home : Tests(4) {
    my $test = shift;
    my $mojo = $test->mojo;

    $mojo->get_ok('/pages/home')->status_is($SERVER_OK);

    $mojo->text_is(
        'title',
        $test->base_title . ' | Home',
        '... and should have the right title'
    );

    $mojo->content_like(
        qr/<body>(?:\s*\S+\s*)+<\/body>/msx,
        '... and should have a non-blank body'
    );

    return;
}

sub get_contact : Tests(3) {
    my $test = shift;
    my $mojo = $test->mojo;

    $mojo->get_ok('/pages/contact')->status_is($SERVER_OK);

    $mojo->text_is(
        'title',
        $test->base_title . ' | Contact',
        '... and should have the right title'
    );

    return;
}

sub get_about : Tests(3) {
    my $test = shift;
    my $mojo = $test->mojo;

    $mojo->get_ok('/pages/about')->status_is($SERVER_OK);

    $mojo->text_is(
        'title',
        $test->base_title . ' | About',
        '... and should have the right title'
    );

    return;
}

sub base_title {
    my ( $self, $base_title ) = @_;

    if ( defined $base_title ) {
        $self->{base_title} = $base_title;
    }

    return $self->{base_title};
}

sub mojo {
    my ( $self, $mojo ) = @_;

    if ( defined $mojo ) {
        $self->{mojo} = $mojo;
    }

    return $self->{mojo};
}

sub setup : Tests(setup) {
    my $test = shift;

    $test->base_title('Mojolicious Sample App');

    $test->mojo( Test::Mojo->new( app => 'SampleApp', max_redirects => 1 ) );

    return;
}

1;

对我们来说,这更像是功能测试,而不是比单元测试

有没有一种方法可以调用控制器的 home 方法并测试其输出,而不需要通过 Test::Mojo 启动服务器实例?

We have created the following simple Mojolicious controller:

package SampleApp::Pages;

# $Id$

use strict;
use warnings;

our $VERSION = '0.01';

use Mojo::Base 'Mojolicious::Controller';

sub home {
    my $self = shift;

    $self->render( 'title' => 'Home' );

    return;
}

sub contact {
    my $self = shift;

    $self->render( 'title' => 'Contact' );

    return;
}

sub about {
    my $self = shift;

    $self->render( 'title' => 'About' );

    return;
}

1;

The corresponding unit tests look as follows:

package Test::SampleApp::Pages;

# $Id$

use strict;
use warnings;

our $VERSION = '0.01';

use Carp;
use English '-no_match_vars';
use Readonly;
use Test::Mojo;
use Test::Most;

use base 'Test::Class';

Readonly my $SERVER_OK => 200;

sub startup : Tests(startup) {
    eval {
        require SampleApp;

        SampleApp->import;

        1;
    } or Carp::croak($EVAL_ERROR);

    return;
}

sub get_home : Tests(4) {
    my $test = shift;
    my $mojo = $test->mojo;

    $mojo->get_ok('/pages/home')->status_is($SERVER_OK);

    $mojo->text_is(
        'title',
        $test->base_title . ' | Home',
        '... and should have the right title'
    );

    $mojo->content_like(
        qr/<body>(?:\s*\S+\s*)+<\/body>/msx,
        '... and should have a non-blank body'
    );

    return;
}

sub get_contact : Tests(3) {
    my $test = shift;
    my $mojo = $test->mojo;

    $mojo->get_ok('/pages/contact')->status_is($SERVER_OK);

    $mojo->text_is(
        'title',
        $test->base_title . ' | Contact',
        '... and should have the right title'
    );

    return;
}

sub get_about : Tests(3) {
    my $test = shift;
    my $mojo = $test->mojo;

    $mojo->get_ok('/pages/about')->status_is($SERVER_OK);

    $mojo->text_is(
        'title',
        $test->base_title . ' | About',
        '... and should have the right title'
    );

    return;
}

sub base_title {
    my ( $self, $base_title ) = @_;

    if ( defined $base_title ) {
        $self->{base_title} = $base_title;
    }

    return $self->{base_title};
}

sub mojo {
    my ( $self, $mojo ) = @_;

    if ( defined $mojo ) {
        $self->{mojo} = $mojo;
    }

    return $self->{mojo};
}

sub setup : Tests(setup) {
    my $test = shift;

    $test->base_title('Mojolicious Sample App');

    $test->mojo( Test::Mojo->new( app => 'SampleApp', max_redirects => 1 ) );

    return;
}

1;

To us, this is more like functionality testing rather than unit testing

Is there a way to call the home method of the controller and test its output that doesn't require starting up a server instance via Test::Mojo?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

爱人如己 2024-11-04 05:35:01

要测试控制器的接线,请使用如下代码。

我们从熟悉的前面内容开始t/pages.t

use Mojolicious;
use Test::More;

现在创建 SampleApp::Pages 的测试子类,记录对 render 的调用。

package TestingPages;
use Mojo::Base 'SampleApp::Pages';

has 'render_called';
has 'render_arg';

sub render {
  my($self,%arg) = @_;
  $self->render_called(1);
  $self->render_arg({ %arg });
}

您的问题使用了 Test::Class,因此请继续该主题。

package Test::SampleApp::Pages;

use base 'Test::Class';
use Test::More;

请注意,不带参数的 die 会传播最近的异常,因此您不必显式编写 $@

sub startup : Test(startup) {
  eval { require SampleApp::Pages; SampleApp::Pages->import; 1 } or die;
}

setup 中,实例化测试子类,将其连接到 Mojolicious 实例,然后关闭日志记录。

sub setup : Test(setup) {
  my($self) = @_;

  my $c = TestingPages->new(app => Mojolicious->new);
  $c->app->log->path(undef);
  $c->app->log->level('fatal');
  $self->{controller} = $c;
}

在 home 测试中,调用控制器的 home 方法并检查结果。

sub home : Tests(2) {
  my($self) = @_;
  my $c = $self->{controller};
  $c->home;
  is $c->render_called, 1, "render called";
  is $c->render_arg->{title}, "Home", "correct title arg";
}

最后,运行测试。

package main;
Test::SampleApp::Pages->runtests;

输出:

$ ./sampleapp.pl test
Running tests from '/tmp/sampleapp/t'.
t/pages.t .. ok   
All tests successful.
Files=1, Tests=2,  1 wallclock secs ( 0.03 usr  0.02 sys +  0.24 cusr  0.03 csys =  0.32 CPU)
Result: PASS

现在您已经了解了如何做到这一点,问题是是否值得这么麻烦。控制器接线应简单。考虑控制器中的任何复杂性是否真的属于测试更加简单的模型。

To test your controller's wiring, use code such as the following.

We begin t/pages.t with familiar front matter.

use Mojolicious;
use Test::More;

Now create a testing subclass of SampleApp::Pages that records calls to render.

package TestingPages;
use Mojo::Base 'SampleApp::Pages';

has 'render_called';
has 'render_arg';

sub render {
  my($self,%arg) = @_;
  $self->render_called(1);
  $self->render_arg({ %arg });
}

Your question used Test::Class, so continue with that theme.

package Test::SampleApp::Pages;

use base 'Test::Class';
use Test::More;

Note that die with no arguments propagates the most recent exception, so you don't have to write $@ explicitly.

sub startup : Test(startup) {
  eval { require SampleApp::Pages; SampleApp::Pages->import; 1 } or die;
}

In setup, instantiate the testing subclass, connect it to a Mojolicious instance, and turn off logging.

sub setup : Test(setup) {
  my($self) = @_;

  my $c = TestingPages->new(app => Mojolicious->new);
  $c->app->log->path(undef);
  $c->app->log->level('fatal');
  $self->{controller} = $c;
}

In the home test, call the controller's home method and inspect the results.

sub home : Tests(2) {
  my($self) = @_;
  my $c = $self->{controller};
  $c->home;
  is $c->render_called, 1, "render called";
  is $c->render_arg->{title}, "Home", "correct title arg";
}

Finally, run your tests.

package main;
Test::SampleApp::Pages->runtests;

Output:

$ ./sampleapp.pl test
Running tests from '/tmp/sampleapp/t'.
t/pages.t .. ok   
All tests successful.
Files=1, Tests=2,  1 wallclock secs ( 0.03 usr  0.02 sys +  0.24 cusr  0.03 csys =  0.32 CPU)
Result: PASS

Now that you see how to do it, the question is whether it's worth all the trouble. Controllers should be simple wiring. Consider whether any complexity in a controller really belongs in a model where testing is much more straightforward.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文