NAME

    Test2::Tools::HTTP - Test HTTP / PSGI

VERSION

    version 0.12

SYNOPSIS

     use Test2::V0;
     use Test2::Tools::HTTP;
     use HTTP::Request::Common;
     
     psgi_add_app sub { [ 200, [ 'Content-Type' => 'text/plain;charset=utf-8' ], [ "Test Document\n" ] ] };
     
     # Internally test the app from within the .t file itself
     http_request(
       # if no host/port/protocol is given then
       # the default PSGI app above is assumed
       GET('/'),
       http_response {
     
         http_code 200;
     
         # http_response {} is a subclass of object {}
         # for HTTP::Response objects only, so you can
         # also use object {} style comparisons:
         call code => 200;
     
         http_content_type match qr/^text\/(html|plain)$/;
         http_content_type_charset 'UTF-8';
         http_content match qr/Test/;
       }
     );
     
     use Test2::Tools::JSON::Pointer;
     
     # test an external website
     http_request(
       # you can also test against a real HTTP server
       GET('http://example.test'),
       http_response {
         http_is_success;
         # JSON pointer { "key":"val" }
         http_content json '/key' => 'val';
       }
     );
     
     done_testing;

    with short names:

     use Test2::Tools::HTTP ':short';
     use HTTP::Request::Common;
     
     app_add { [ 200, [ 'Content-Type => 'text/plain' ], [ "Test Document\n" ] ] };
     
     req (
       GET('/'),
       res {
         code 200;
         message 'OK';
         content_type 'text/plain';
         content match qr/Test/;
       },
     );
     
     done_testing;

DESCRIPTION

    This module provides an interface for testing websites and PSGI based
    apps with a Test2 style comparisons interface. You can specify a PSGI
    app with a URL and responses from that URL will automatically be routed
    to that app, without having to actually need a separate server process.
    Requests to URLs that haven't been registered will be made against the
    actual networks servers as appropriate. You can also use the user agent
    returned from http_ua to make requests against PSGI apps.
    LWP::UserAgent is the user agent used by default, but it is possible to
    use others assuming an appropriate user agent wrapper class is
    available (Test2::Tools::HTTP::UA).

    By default it uses long function names with either a http_ or psgi_app_
    prefix. The intent is to make the module usable when you are importing
    lots of symbols from lots of different testing tools while reducing the
    chance of name collisions. You can instead import :short which will
    give you the most commonly used tools with short names. The short names
    are indicated below in square brackets, and were chosen to not conflict
    with Test2::V0.

FUNCTIONS

 http_request [req]

     http_request($request);
     http_request($request, $check);
     http_request($request, $check, $message);
     http_request([$request, %options], ... );

    Make a HTTP request. If there is a client level error then it will fail
    immediately. Otherwise you can use a object {} or http_request
    comparison check to inspect the HTTP response and ensure that it
    matches what you expect. By default only one request is made. If the
    response is a forward (has a Location header) you can use the
    http_tx-location> method to make the next request.

    Options:

    follow_redirects

      This allows the user agent to follow redirects.

 http_response [res]

     my $check = http_response {
       ... # object or http checks
     };

    This is a comparison check specific to HTTP::Response objects. You may
    include these subchecks:

  http_code [code]

     http_response {
       http_code $check;
     };

    The HTTP status code should match the given check.

  http_message [message]

     http_response {
       http_message $check;
     };

    The HTTP status message ('OK' for 200, 'Not Found' for 404, etc) should
    match the given check.

  http_content [content]

     http_response {
       http_content $check;
     };

    The response body content. Attempt to decode using the HTTP::Message
    method decoded_content, otherwise use the raw response body. If you
    want specifically the decoded content or the raw content you can use
    call to specifically check against them:

     http_response {
       call content => $check1;
       call decoded_content => $check2;
     };

  http_is_info, http_is_success, http_is_redirect, http_is_error,
  http_is_client_error, http_is_server_error

     http_response {
       http_is_info;
       http_is_success;
       http_is_redirect;
       http_is_error;
       http_is_client_error;
       http_is_server_error;
     };

    Checks that the response is of the specified type. See HTTP::Status for
    the meaning of each of these.

  http_isnt_info, http_isnt_success, http_isnt_redirect, http_isnt_error,
  http_isnt_client_error, http_isnt_server_error

     http_response {
       http_isnt_info;
       http_isnt_success;
       http_isnt_redirect;
       http_isnt_error;
       http_isnt_client_error;
       http_isnt_server_error;
     };

    Checks that the response is NOT of the specified type. See HTTP::Status
    for the meaning of each of these.

  http_headers [headers]

     http_response {
       http_headers $check;
     };

    Check the HTTP headers as converted into a Perl hash. If the same
    header appears twice, then the values are joined together using the ,
    character. Example:

     http_request(
       GET('http://example.test'),
       http_response {
         http_headers hash {
           field 'Content-Type' => 'text/plain;charset=utf-8';
           etc;
         };
       },
     );

  http_header [header]

     http_response {
       http_header $name, $check;
     };

    Check an HTTP header against the given check. Can be used with either
    scalar or array checks. In scalar mode, any list values will be joined
    with , character. Example:

     http_request(
       GET('http://example.test'),
       http_response {
     
         # single value
         http_header 'X-Foo', 'Bar';
     
         # list as scalar, will match either:
         #     X-Foo: A
         #     X-Foo: B
         # or
         #     X-Foo: A,B
         http_header 'X-Foo', 'A,B';
     
         # list mode, with an array ref:
         http_header 'X-Foo', ['A','B'];
     
         # list mode, with an array check:
         http_header 'X-Foo', array { item 'A'; item 'B' };
       },
     );

  http_content_type [content_type], http_content_type_charset [charset]

     http_response {
       http_content_type $check;
       http_content_type_charset $check;
     };

    Check that the Content-Type header matches the given checks.
    http_content_type checks just the content type, not the character set,
    and http_content_type_charset matches just the character set. Hence:

     http_response {
       http_content_type 'text/html';
       http_content_type_charset 'UTF-8';
     };

  http_content_length [content_length]

     http_response {
       http_content_length $check;
     };

    Check that the Content-Length header matches the given check.

  http_content_length_ok [content_length_ok]

     http_response {
       http_content_length_ok;
     };

    Checks that the Content-Length header matches the actual length of the
    content.

  http_location [location], http_location_uri [location_uri]

     http_response {
       http_location $check;
       http_location_uri $check;
     };

    Check the Location HTTP header. The http_location_uri variant converts
    Location to a URI using the base URL of the response so that it can be
    tested with Test2::Tools::URL.

 http_tx [tx]

     my $req    = http_tx->req;
     my $res    = http_tx->res;
     my $bool   = http_tx->ok;
     my $string = http_tx->connection_error;
     my $url    = http_tx->location;
     http_tx->note;
     http_tx->diag;

    This returns the most recent transaction object, which you can use to
    get the last request, response and status information related to the
    most recent http_request.

    http_tx->req

      The HTTP::Request object.

    http_tx->res

      The HTTP::Response object.

      Warning: Depending on the user agent class in use, in the case of a
      connection error, this may be either a synthetic response or not
      defined. For example LWP::UserAgent produced a synthetic response,
      while Mojo::UserAgent does not produce a response in the event of a
      connection error.

    http_tx->ok

      True if the most recent call to http_request passed.

    http_tx->connection_error.

      The connection error if any from the most recent http_reequest.

    http_tx->location

      The Location header converted to an absolute URL, if included in the
      response.

    http_tx->note

      Send the request, response and ok to Test2's "note" output. Note that
      the message bodies may be decoded, but the headers will not be
      modified.

    http_tx->diag

      Send the request, response and ok to Test2's "diag" output. Note that
      the message bodies may be decoded, but the headers will not be
      modified.

 http_base_url

     http_base_url($url);
     my $url = http_base_url;

    Sets the base URL for all requests made by http_request. This is used
    if you do not provide a fully qualified URL. For example:

     http_base_url 'http://httpbin.org';
     http_request(
       GET('/status/200') # actually makes a request against http://httpbin.org
     );

    If you use psgi_add_app without a URL, then this is the URL which will
    be used to access your app. If you do not specify a base URL, then
    localhost with a random unused port will be picked.

 http_ua [ua]

     http_ua(LWP::UserAgent->new);
     my $ua = http_ua;

    Gets/sets the LWP::UserAgent object used to make requests against real
    web servers. For tests against a PSGI app, this will NOT be used. If
    not provided, the default LWP::UserAgent will call env_proxy and add an
    in-memory cookie jar.

 psgi_app_add [app_add]

     psgi_app_add $app;
     psgi_app_add $url, $app;

    Add the given PSGI app to the testing environment. If you provide a
    URL, then requests to that URL will be intercepted by http_request and
    routed to the app instead of making a real HTTP request via
    LWP::UserAgent.

 psgi_app_del [app_del]

     psgi_app_del;
     psgi_app_del $url;

    Remove the app at the given (or default) URL.

 psgi_app_guard [app_guard]

     my $guard = psgi_app_guard $app;
     my $guard = psgi_app_guard $url, $app;
     my $guard = psgi_app_guard $url, $app, ...;

    Similar to psgi_app_add except a guard object is returned. When the
    guard object falls out of scope, the old apps are restored
    automatically. The intent is for this to be used in subtests or other
    scoped blocks to temporarily override the internet or other PSGI apps.

     psgi_add_add 'http://foo.test' => sub { ... };
     
     subtest 'mysubtest' => sub {
       my $guard = psgi_app_guard
         'http://foo.test' => sub { ... },
         'https://www.google.com' => sub { ... };
     
       http_request
         # gets the foo.test for this scope.
         GET('http://foo.test'),
         http_response {
           ...
         };
     
       http_request
         # gets the mock google
         GET('https://www.google.com'),
         http_response {
           ...;
         };
     };
     
     http_request
       # gets the original foo.test mock
       GET('http://foo.test'),
       http_response {
         ...;
       };
     
     http_request
       # gets the real google
       GET('https://www.google.com'),
       http_response {
         ...;
       };

    Because calling a function that returns a guard in void context is
    usually a mistake, this function will throw an exception if you attempt
    to call it in void context.

SEE ALSO

    Test::Mojo

      This is a very capable web application testing module. Definitely
      worth checking out, even if you aren't developing a Mojolicious app
      since it can be used (with Test::Mojo::Role::PSGI) to test any PSGI
      application.

    Plack::Test

      Also allows you to make HTTP::Request requests against a PSGI app and
      get the appropriate HTTP::Response response back. Doesn't provide any
      special tools for interrogating that response. This module in fact
      uses this one internally.

    Test::LWP::UserAgent

      This is a subclass of LWP::UserAgent that can return responses from a
      local PSGI app, similar to the way this module instruments an
      instance of LWP::UserAgent for similar purposes. The limitation to
      this approach is that it cannot be used with classes which cannot be
      used with subclasses of LWP::UserAgent. By contrast, this module can
      instrument an existing LWP::UserAgent object without having to
      rebless it into another class or other such shenanigans. If you can
      at least get access to another class's user agent instance, it can be
      used with Test2::Tools::HTTP's mock website system. Doesn't work with
      anything that is not an LWP::UserAgent object.

    LWP::Protocol::PSGI

      Provides a similar functionality to Test::LWP::UserAgent, but
      registers apps globally using LWP::Protocol so that you do not need
      access to a specific LWP::UserAgent object. Does not work with
      anything that is not an LWP::UserAgent object. Test2::Tools::HTTP::UA
      provides similar functionality, but is an abstraction layer which can
      be used with any appropriately adapted user agent class or instance,
      although we use LWP::UserAgent by default. Support for
      Mojo::UserAgent and HTTP::AnyUA is available, although not bundled
      with this distribution. One advantage of this abstraction is that it
      can be used to instrument either a single instance or all objects
      belonging to a particular class.

AUTHOR

    Graham Ollis <plicease@cpan.org>

COPYRIGHT AND LICENSE

    This software is copyright (c) 2018-2022 by Graham Ollis.

    This is free software; you can redistribute it and/or modify it under
    the same terms as the Perl 5 programming language system itself.

