Wednesday, March 2, 2011

Day 32 - Moving to Test::Nginx

I told you yesterday I was going to move to Test::Nginx (a perl module contributed by agentzh) to test my RRD module. So far, I had been testing it with Python and just wanted to move my tests (10 or so) to this testing framework.

First thing first, I tried to do a relatively clean install of the module for my distro. The problem is, of course that there is no Test::Nginx packaged for my Fedora Core 14 but there are a lot of packages for other CPAN modules Test::Nginx needs. So, it all ended up like that (as user root or with sudos, whatever suits you):
yum install perl-CPAN perl-Test-Base perl-Test-LongString
yum install perl-List-MoreUtils
perl -MCPAN -e 'install Test::Nginx'

And then, the nightmare started. I haven't touched perl since 1994. I don't remember very well, but I think that back then perl 5 was brand new and kind of the hot thing (a bit like Ruby nowadays). Just to tell you that I have been out of touch and that might explain some of my difficulties. But not all...

The first thing that took me off-guard is that there are actually 2 modules in Test::Nginx:
  1. Test::Nginx::LWP
  2. Test::Nginx::Socket
Except for the fact that Socket is non-blocking and that LWP is based on LWP, you cannot really tell the differences. So, I didn't even know which one to use. Piotr Sikora pointed me in the right direction with this: description of Test::Nginx in the source file. That is in the git repository but not in the CPAN yet.

I'll try to list the problems I ran into and how I fixed them.

How do you send a body ?. Test::Nginx::LWP has a request_body parameter but Test::Nginx::Socket does not. Some requests (like PUT) must have one. The trick is to actually put the body in the request_eval parameter. Maybe there is another trick but at least this one seems to work:
=== TEST 1: PUT is not allowed
--- config
    location /rrd/taratata {
        rrd /var/rrd/taratata.rrd;
--- request_eval
"PUT /rrd/taratata

--- response_body_like: support.*GET.*POST
--- error_code: 405

Headers. Second problem I fought with: how do you set a specific header. In my case I wanted to test that requests for POST with anything but application/x-www-urlencoded failed with the appropriate message. There, you have to use the more_headers parameter in something like:
=== TEST 3: POST bad content type
--- config
    location /rrd/taratata {
        rrd /var/rrd/taratata.rrd;
--- more_headers
Content-Type: text/plain
--- request
POST /rrd/taratata
--- response_body_like: content type
--- error_code: 405

All this is pretty much lack of documentation of Test::Nginx and I'll get used to it and maybe even give agentzh a hand in documenting the stuff.

Now, a few things that really annoyed me:
  1. I could not find a way to run only a specific test. Right now my file has about 7 tests but I cannot find a way to tell it to test only one of them with prove. I tried redirecting the input in 10 different ways but it looks to me like all the tests in the file are run whatever you do. It was so easy in python (sniff): python TestMethods.test_POST_BIG
  2. I have the same nginx config for all my tests. I would really love to write it only once (as opposed to putting it in every test). I cannot complain as I did not have this functionality in python. Room for improvement on the module.
  3. The nginx server stops and starts each time. Now, this plus the fact that the log file gets reset at each restart+the fact that you cannot select which test to run+the fact that tests are run in random order makes figuring out things a bit consuming. Here again, there is room for improvement, I think.

Now, there are still a few things I need to figure out:
  • How do you introduce delay when you want to send data as chunks ?
  • How do you get multiple requests in one test ? You see, I had this problem where running any request after an error resulted in an error. I was not calling rrd_clear_error, stupid me. But the fact and the matter is that I wrote a test for this in python and don't want to let it go in the migration.

So, the migration was not a breeze and I don't know if it was worth it. I guess I'll get used to it and it would really be not "open-source" to do my own testing thing in python when people already invested time and effort in another tool. So, I'll try to help improving this one.

1 comment:

  1. 1. Running an individual test case is trivial. Just adding --- ONLY to the test block and run the file. IMHO, it's much easier than Python's way of explicitly specifying a test file name and a test case name.
    2. You can use either an nginx variable or a subclass to avoid duplication.
    3. You can specify the environment TEST_NGINX_NO_CLEAN=1 to make the test scaffold not to kill the nginx after quit. Well, it's convenient to combine with the --- ONLY trick. Plus, there is a TEST_NGINX_USE_HUP=1 environment that makes the test scaffold use HUP reload for the next test block.