Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

OK to Include CHI File Data in module Tests ?

by localshop (Monk)
on Oct 20, 2018 at 04:52 UTC ( [id://1224376]=perlquestion: print w/replies, xml ) Need Help??

localshop has asked for the wisdom of the Perl Monks concerning the following question:

Trying to refine WebService::GoogleAPI::Client which uses CHI to cache resources pulled from Google API Discovery. In the package tests I'd like to maximise the coverage but don't wish to impose a whole bunch of HTTP requests every time tests are run. I was thinking that by bundling in a CHI::Driver::File directory for use by the tests I could default to using this and perhaps provide a switch to allow live requests for local dev testing where needed.

I am concerned that the cache files won't be portable across platforms but I can probably catch that out.

  1. is this approach missing any obvious bad practice
  2. are any CPAN modules that do this that I could review?
  3. any advice on testing against network resources or cached data appreciated.

Another option that occurred to me after posting this question is to create my own CHI::Driver subclass and use that for testing with some hard coded values.

Many thanks for any advice.

  • Comment on OK to Include CHI File Data in module Tests ?

Replies are listed 'Best First'.
Re: OK to Include CHI File Data in module Tests ?
by 1nickt (Canon) on Oct 20, 2018 at 11:51 UTC

    Hi, are you seeking to test the processing of data received from the API? It sounds like that, by "don't wish to impose a whole bunch of HTTP requests every time tests are run." If so, I would think that you would want to leave your caching layer alone (presumably it defaults to the File CHI driver) so that it can be exercised fully. All you would want to mock in that case would be the HTTP requests themselves.

    At ($job - 1) we used a customized version of Test::LWP::Recorder for this. You run your tests once with live calls, recording the entire HTTP conversation, and thereafter run them using the recorded data to mock the calls. It was quite useful, but of course involved making and recording the calls as part of updating the test suite.

    At $job we handle it differently, overriding LWP::UserAgent methods to inject an HTTP::Response object created from known static data. This approach allows the code to be exercised all the way up to actually sending the request, and then all the way from when LWP receives the response. In fact I hack LWP::UserAgent::send_request to print a notice any time it actually runs (ie makes a live call), to reveal any leaks. We have a test helper class that contains the code to generate the override subs needed, since often a call to our API results in more than one call out to the provider, and you'll need a routine that hands back a sequence of mocked responses. The static data representing the mocked responses can be in the helper class or in the given test file itself. At its core the code is something like this:

    local *LWP::UserAgent::get = sub { HTTP::Response->new(200, undef, und +ef, $response_data) };
    ... and in our test files we are calling it something like this:
    my $test = $helper->run_test({ # wraps methods get_ok, post_ok etc f +rom Test::Mojo uri => $some_endpoint, # what we are testing status => 201, # expected status of call to our API method => 'post', # HTTP method of the request data => $json_args, # what the user would pass in the cal +l to our API override => [{ method => 'get', # HTTP method of the first mocked ext +ernal call status => 200, # mocked return status of the first m +ocked call content => $some_data, # mocked response content from the fi +rst mocked call }, { method => 'post', # HTTP method of the second mocked ex +ternal call status => 201, # mocked return status of the second +mocked call content => $other_data, # mocked response content from the se +cond mocked call }], }); $test->json_is('/result/foo', 'bar'); # Test::Mojo methods used to tes +t the response

    Hope this helps!


    The way forward always starts with a minimal test.
      Excellent - I hadn't thought of going down a level and that makes a lot of sense. I'm still exploring how best to do this but you've certainly given me a jump start. As I was using the Mojo::UserAgent I had a quick sniff at Mojo::UserAgent::Mockable but I'm not yet convinced that this is the way to go. Although it looks perfect in a way, I have concerns about it breaking tests in some environments.

      Most of my concern stems from the very long install time locally and the number of dependencies suggesting it might be a little heavy just to provide a test capability. I haven't written it off entirely as an option though but will focus my attention initially on injecting responses similar to your suggestion.

      Thanks heaps for the tips and insight from your experience

Re: OK to Include CHI File Data in module Tests ?
by localshop (Monk) on Oct 26, 2018 at 20:03 UTC
    I started out using an in-memory driver for CHI and pre-warmed that with a few requests and this worked but liked the idea of injecting at the User Agent level. After a little wrangling with the Mojo::UserAgent classes which are a little new to me I ended up working out how to inject the HTTP response with a little help from Sub::OverRide . Essentially in my test scripts I can now just inject a response like so:
    my $override3 = Sub::Override->new('Mojo::Transaction::res', sub { my $res2 = Mojo::Message::Response->new ; $res2->code( 200 ); $res2->body( "TESTED FINE" ); return $res2; });

    I also came across Mojo::UserAgent::Mockable but it took quite a while to install and seemed to introduce a lot of new dependencies so I put that on the backburner - may be worth a look at again in the future.

    It also allows you to continue with new overrides as you progress but one little thing that caught me out was that each override needs to be assigned to a distinct variable and you cannot re-use the same one. I expect that this could be avoided by blocking each instance as described in the docs but the approach I'm using now serves my needs well.

    As I continue to work at improving my test writing I'll def. use this injection approach or a refinement of it going forward.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1224376]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (6)
As of 2024-04-24 01:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found