If you haven’t already, please read the beginner’s tutorial first.
If you look at the filename for the usecase teaser image we have
copied into our new-assets
folder (see the RoboHydra front page
example in the basic tutorial), you’ll notice that it contains a
version number (the v1
bit). Now say that we don’t want those
version numbers in our filenames (eg. because we want our local files
to work regardless of the current version on the production front
page). For these situations, RoboHydra has a simple solution:
chaining. The idea behind chaining is that each request handler can
accept an extra parameter next
, a function that allows the head to
call any heads below it. As that function can be called with any
request and response objects, you can do interesting things like
tweaking the request being processed (eg. change the URL, add or
remove headers, etc.) or tweaking the response being returned
(eg. change the body or the headers). You could even call the function
several times, to retry a request, combine the responses of several
requests, or whatever else you might need.
In this example, we’re simply going to strip the .vXXX
part of the
filenames inside /static/img/
, where XXX
are numbers. To do so,
simply add a new head at the top and tweak the documentRoot
in the
second head:
Now, create a new directory new-assets-unversioned
with the same
files, but renaming usecases-teaser.v1.png
to
usecases-teaser.png
. Once you have the new files, start RoboHydra
again with robohydra -n -P rhimages
. Everything should keep working
as before, and will keep working even if the RoboHydra front page
changes the version number in the URLs.
But what about tweaking the response we get from some other head?
That’s interesting, too. Let’s turn instances of “server” into
“SERVER”. To do that, we have to create another head before the
RoboHydraHeadProxy
. This new head will call the proxying head with
the next
function, but passing a fake response object. Then it will
tweak the body of that response, then return that tweaked
response. It sounds complicated, but the code is simple enough:
The Response
constructor receives an argument, a function to be
executed when the response ends. We use this function to inspect the
response we got from the other head, tweak it and send our own
response. Also, note how we remove the “Accept-Encoding” header from
the request before proxying it: this is to avoid that the response
comes compressed. We could have also received the normal request and
uncompress it, but this is simpler for this example.
In fact, this is a relatively common thing to do, so there’s a head
for specifically this purpose: RoboHydraHeadFilter
. Using this head,
you don’t have to care about compression in the server response, and
the code is much more compact and readable. A new version of the above
head using RoboHydraHeadFilter
could be:
If you’re serious about testing your client code, you’re probably going to end up writing a test suite of some sort. Not necessarily completely automated, but at least you will want an easy way to change RoboHydra’s behaviour to match each test scenario.
Now, you could do that with everything we have learned up until now: you could define many heads for the same path and enable or disable all the relevant heads from the web interface. But that is complex and tiring, not to mention very error prone. Alas, RoboHydra has a much better way to deal with that situation: defining scenarios. A scenario consists of a collection of heads. These heads are active only when the scenario they belong to is active (and only one scenario can be active at any given time).
Thus, if you need to easily restore a certain behaviour in RoboHydra, you can define, in a named scenario, a collection of heads that define that behaviour. Then, every time you need to restore that behaviour, you start the corresponding scenario.
Let’s go back to the first example in the first part of the
tutorial. In it, we made RoboHydra return certain “search results” in
the /foo
path. If we’re serious about testing that client, we
probably want RoboHydra to return different things, like no results, a
couple of results or even an internal server error. Let’s implement
those three cases as scenarios. Create a new file
robohydra/search/index.js
with the following contents:
Once saved, run RoboHydra like so:
robohydra -n -P search
You can see the available scenarios, which one is active (if any) and
start and stop them in the scenario admin
interface. When
starting RoboHydra there won’t be any active scenario, so
/foo
will say “This is the default
behaviour of /foo”. However, if we go to the scenario interface and
start any of the scenarios, we’ll have the desired behaviour in
/foo
. Go now to the scenario interface and experiment a bit with the
results you get when starting the different scenarios.
Note how when you start the last scenario, RoboHydra will show some
instructions and some expected behaviour (the instructions
field is
interpreted as Markdown). This can be very handy if you want to build
some kind of acceptance test suite of your client. And in case you
want to automate the testing of the client, you will also want to
automate the starting/switching of scenarios in RoboHydra. In that
case, you can use the RoboHydra REST API.
If you’re preparing a more formal test suite for your project, you may want to not only check how the client behaves with different responses from the server, but also if the client requests are well-formed and correct. RoboHydra heads can contain assertions that will be counted as part of the current active scenario.
Let’s say we are still testing the client of that search engine in the
previous section, and we want to make sure we don’t have character
encoding problems. Thus, we’ll add a scenario that checks that the
client sent a correctly formed, UTF-8 search string. We can start by
adding a new scenario, nonAsciiSearchTerm
, with the following
definition:
Note that now the getBodyParts
function accepts a second parameter,
modules
. This second parameter is an object with available modules
as its properties. The only module available as of today is assert
:
an object with all the functions in the standard, assert
module from Node. This
version, though, is special in two ways: first, it’s tied to the
server, which allows RoboHydra to fetch the results; second, test
failures won’t throw an exception, but instead return false. This
behaviour is much more useful because it allows you to respond with
whatever content you want in case of failure.
Now, if you start the nonAsciiSearchTerm
scenario and make a request
with the wrong string
(eg. http://localhost:3000/foo?q=blaabaersyltetoy),
you’ll get an error response, and see the test failure in the
scenario index
page. If you send
the correct string
(eg. http://localhost:3000/foo?q=blåbærsyltetøy),
however, you’ll see the one-result response and the test pass in the
scenario index page. In case you want to access this information in an
automated fashion, you can get the results in JSON format at the URL
http://localhost:3000/robohydra-admin/rest/test-results.
Especially when you are using RoboHydra for test suites inside a company, you might want to have a single RoboHydra installation for the whole organisation. That is, instead of every developer or tester having one in their own private workstation, having one shared RoboHydra server for everyone.
If you used RoboHydra as we have seen so far, that would be a problem because when two users tried to use the test suite at the same time, the second user would reset the active scenario while the first user is still testing someting. To solve that problem, the RoboHydra server permits having more than one “hydra” (“hydra” being a collection of heads with their own state) and choose between them using the summoner. The idea is that upon receiving a request, the summoner will give a different hydra to every user (eg. by checking cookies): that way many users can use the same RoboHydra server without interfering with one another. The default summoner will always summon the same hydra (ie. it’s for a single user), but you can provide the function that receives the incoming request object and returns the name of the hydra that should process that request.
Let’s make a simple, multi-user RoboHydra server. We’ll start by
deciding how we’re going to tell the different users apart: although a
common way to do so in the real world would be to use cookies, as a
simplification we’ll just use the browser user agents. Create a file
robohydra/ua-summoner/index.js
with the following contents:
In this case every user agent will be considered a different user. In
other words, every browser you have installed (including different
versions!) will get its requests served by a different hydra. To test
this, start RoboHydra like robohydra -P ua-summoner
, fire two or
three different browsers and go to
/robohydra-admin/ with all
of them. You’ll notice on the bottom left that the hydra name is
different for every browser. If you create new heads with one browser
using the admin UI, the other browser will not see those new
heads. That means all browsers you try with can work independently of
each other, but all of them using the same server!
And this is the end of the advanced RoboHydra tutorial. Now you have learned about all features and it’s just a matter of experimenting and creating your own plugins.
For more information, check the reference documentation.