test-support

Test support

MVC::Keayl::TestSupport drives requests through an application in-process and asserts on the result, with helpers for routing, controllers, mailers, jobs, and cable. Every assertion throws X::MVC::Keayl::Test::AssertionFailed on failure and returns on success, so it works with Test (lives-ok/dies-ok) and with BDD::Behave (expect({ ... }).not.to.throw).

Integration sessions

An IntegrationSession wraps any application endpoint (a dispatcher or the full Application.endpoint) and issues requests, persisting the cookie jar across them so sessions and flash survive:

use MVC::Keayl::TestSupport;

my $session = IntegrationSession.new(app => $dispatcher);

$session.get('/login');
$session.post('/sessions', body => 'user=ada');
$session.get('/dashboard');     # the session cookie from the login is sent back

get, post, put, patch, and delete take a target and optional headers and body. The last response is session.response. follow-redirect issues the redirected request after a 3xx.

Response assertions

$session.assert-response(200);          # or a name: 'ok', 'not-found', ...
$session.assert-redirected-to('/login');
$session.assert-select('Welcome');      # substring or a Regex over the body

assert-select matches against the response body (a string substring or a regex), with an optional text => to require additional content.

Live server

IntegrationSession drives the app in memory, which covers most tests. When a test needs a real listening socket — driving the app from an external HTTP client or a browser — LiveServer serves a built endpoint over the Cro adapter on an ephemeral port:

use MVC::Keayl::TestSupport;

my $server = LiveServer.new(app => $application.endpoint).start;

my $url = $server.url('/dashboard');   # http://127.0.0.1:<port>/dashboard
# ... drive $url with any HTTP client or browser ...

$server.stop;

new picks a free localhost port; override it with host, port, or scheme. start binds the socket and returns the server, base-url and url($path) build addresses against it, and stop shuts it down. Each LiveServer defaults to a distinct port, so several can run at once.

Routing assertions

These check a router both ways:

assert-recognizes($router, 'GET', '/widgets/5', matching => %( controller => 'widgets', action => 'show', id => '5' ));
assert-generates($router, 'widget', '/widgets/5', 5);
assert-routing($router, 'widget', 'GET', '/widgets/5', matching => %( action => 'show' ), 5);

Controller and view introspection

RecordingRenderer stands in for the view renderer and records what was rendered, so a controller test can dispatch an action and inspect the result:

my $renderer   = RecordingRenderer.new;
my $controller = WidgetsController.new(view-renderer => $renderer);
$controller.dispatch('show');

assert-rendered($renderer, 'show');
assert-assigned($controller, 'widget', $widget);

Component helpers

Mailer, job, and cable activity is asserted around a block:

assert-emails(1, { UserMailer.new(delivery => $test-delivery).deliver('welcome', $user) });
delivered-emails();   # the collected test deliveries

assert-enqueued-jobs(2, $test-adapter, { ImportJob.perform-later($a); ImportJob.perform-later($b) });
perform-enqueued-jobs($test-adapter);

assert-broadcasts($pubsub, 'room:1', 2, { $pubsub.broadcast('room:1', 'a'); $pubsub.broadcast('room:1', 'b') });
assert-stream-subscribed($channel, 'room:1');

With BDD::Behave

The same helpers read naturally in a behave spec by wrapping the assertion in a block:

describe 'the dashboard', {
  let(:session, { IntegrationSession.new(app => $app.endpoint) });

  it 'greets a signed-in user', {
    session.get('/dashboard');
    expect({ session.assert-select('Welcome') }).not.to.throw;
  }
}

MVC::Keayl v0.9.1

Model-View-Controller web framework

Authors

  • Greg Donald

License

Artistic-2.0

Dependencies

Cro::HTTPCrypt::RandomDigest::HMACDigest::SHA1::NativeOpenSSLMIME::Base64YAMLishORM::ActiveRecord:auth<zef:gdonald>Template::HAML:auth<zef:gdonald>

Provides

  • MVC::Keayl
  • MVC::Keayl::APIController
  • MVC::Keayl::ActionText
  • MVC::Keayl::ActionText::Repository
  • MVC::Keayl::ActionText::RichTextable
  • MVC::Keayl::Adapter
  • MVC::Keayl::Adapter::Cro
  • MVC::Keayl::Adapter::Test
  • MVC::Keayl::Application
  • MVC::Keayl::Assets
  • MVC::Keayl::Assets::Serving
  • MVC::Keayl::CLI
  • MVC::Keayl::CSRF
  • MVC::Keayl::Cable::Broadcasting
  • MVC::Keayl::Cable::Channel
  • MVC::Keayl::Cable::Connection
  • MVC::Keayl::Cable::PubSub
  • MVC::Keayl::Cable::PubSub::External
  • MVC::Keayl::Cable::PubSub::InMemory
  • MVC::Keayl::Cache
  • MVC::Keayl::Caching
  • MVC::Keayl::Config
  • MVC::Keayl::Controller
  • MVC::Keayl::Cookies
  • MVC::Keayl::Credentials
  • MVC::Keayl::Dispatcher
  • MVC::Keayl::Endpoint
  • MVC::Keayl::Engine
  • MVC::Keayl::ErrorReporter
  • MVC::Keayl::ErrorReporting
  • MVC::Keayl::Errors
  • MVC::Keayl::ExceptionPage
  • MVC::Keayl::Flash
  • MVC::Keayl::HealthController
  • MVC::Keayl::Helpers::Asset
  • MVC::Keayl::Helpers::DateTime
  • MVC::Keayl::Helpers::Form
  • MVC::Keayl::Helpers::Number
  • MVC::Keayl::Helpers::Options
  • MVC::Keayl::Helpers::Tag
  • MVC::Keayl::Helpers::Text
  • MVC::Keayl::Helpers::Url
  • MVC::Keayl::HttpAuthentication
  • MVC::Keayl::I18n
  • MVC::Keayl::I18n::Locale
  • MVC::Keayl::I18n::Pluralization
  • MVC::Keayl::Job
  • MVC::Keayl::Job::GlobalID
  • MVC::Keayl::Job::QueueAdapter
  • MVC::Keayl::Job::QueueAdapter::Async
  • MVC::Keayl::Job::QueueAdapter::Database
  • MVC::Keayl::Job::QueueAdapter::Inline
  • MVC::Keayl::Job::QueueAdapter::Test
  • MVC::Keayl::Live
  • MVC::Keayl::LogEvent
  • MVC::Keayl::Logger
  • MVC::Keayl::Mail
  • MVC::Keayl::Mailbox
  • MVC::Keayl::Mailbox::Ingress
  • MVC::Keayl::Mailbox::Router
  • MVC::Keayl::Mailer
  • MVC::Keayl::Mailer::Delivery
  • MVC::Keayl::Mailer::Delivery::File
  • MVC::Keayl::Mailer::Delivery::SMTP
  • MVC::Keayl::Mailer::Delivery::Test
  • MVC::Keayl::Mailer::DeliveryJob
  • MVC::Keayl::Mailer::Preview
  • MVC::Keayl::Middleware
  • MVC::Keayl::Middleware::Database
  • MVC::Keayl::Middleware::HostAuthorization
  • MVC::Keayl::Middleware::Logger
  • MVC::Keayl::Middleware::RequestId
  • MVC::Keayl::Middleware::SSL
  • MVC::Keayl::Middleware::SecureHeaders
  • MVC::Keayl::Middleware::Static
  • MVC::Keayl::MiddlewareStack
  • MVC::Keayl::Mime
  • MVC::Keayl::Notifications
  • MVC::Keayl::PWAController
  • MVC::Keayl::ParameterFilter
  • MVC::Keayl::Parameters
  • MVC::Keayl::Params
  • MVC::Keayl::Request
  • MVC::Keayl::Response
  • MVC::Keayl::Router
  • MVC::Keayl::Routing
  • MVC::Keayl::Routing::Mount
  • MVC::Keayl::Routing::PathPattern
  • MVC::Keayl::Routing::Redirect
  • MVC::Keayl::Routing::Resources
  • MVC::Keayl::Routing::Route
  • MVC::Keayl::Routing::RouteMatch
  • MVC::Keayl::Routing::UrlHelpers
  • MVC::Keayl::SafeString
  • MVC::Keayl::Secrets
  • MVC::Keayl::Session
  • MVC::Keayl::Storage
  • MVC::Keayl::Storage::Attached
  • MVC::Keayl::Storage::Repository
  • MVC::Keayl::Storage::Service
  • MVC::Keayl::Storage::Serving
  • MVC::Keayl::Storage::Variant
  • MVC::Keayl::TestSupport
  • MVC::Keayl::View
  • MVC::Keayl::View::Context
  • MVC::Keayl::View::Handler
  • MVC::Keayl::View::Handler::HAML

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.