conquest

a modern restful api and load tester

conquest (CONcurrent reQUESTs) is a modern and simple tool that allows you to easily test your restful apis and measure your code's performance under stress by writing a few javascript lines.

installation

go get -u github.com/brsyuksel/conquest

cli

conquest has an easy-to-use cli tool witsh a few parameters.

$ conquest -h
Usage of conquest:
  -c="conquest.js": conquest js file path
  -o="": output file for summary
  -s=false: do transactions in sequential mode
  -t="30s": duration for performing transactions. Use s, m, h modifiers
  -u=10: concurrent users.
  -v=false: print failed requests
          

by default, conquest looks for conquest.js in currrent directory. also, you can specify another file path via -c parameter. if you want to write report of conquest to a file, you can use -o parameter with a file path. you can also want to see failed request header, use -v parameter for this. conquest performs transactions in randomized mode; if you want conquest to run sequential, use -s parameter; otherwise you can specify duration for transactions with using -t parameter.

javascript api

first, create a file named conquest.js where you want. our main variable is: conquest. conquest object has just 8 methods which are explained below.

conquest.Host(host);

you can define your target host by using this method.

conquest.Proto(proto);

defines which http protocol to use. the proto parameter can be "HTTP/1.1" or "HTTP/1.0"

conquest.ConquestHeaders();

conquest adds some headers to all requests when you call this method. this header data block may be very useful for your application that it can determine requests for testing. the header data looks like below:
User-Agent: conquest [VERSION]
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache

conquest.Headers(object);

you can define some headers for all requests. the object parameters can only contain string keys and string values. ex: {"X-Test", "True"}

conquest.Cookies(object);

you can define basic cookies as key-value couples for all requests. the object parameters can only contain string keys and string values. ex: {"cookie_name": "cookie_values"}

conquest.Sequential();

makes conquest to perform transactions in sequential mode. the every and finally transactions (we will discuss later) will be performed always as sequential but cases/then transactions can be performed as sequential or random.

conquest.Duration(duration);

you can specify duration for performing randomized cases transaction. it must be explicitly provided when you do not prefer to use sequential mode. use "h", "m", "s" modifiers to specify duration. ex: "3m30s". default value is "30s"

conquest.Dump();

dump method does not allow the performing transactions and dumps transactions as json. it can be very useful when you have doubts about conquest.js file.

conquest.Users(user_num, function(users){});

And the goal method is conquest.Users(). the user_num parameter specifies the number of concurrent users. the second parameter is a function that will be called by conquest with users parameters. we will define conquest's behavior for transactions by using it.

let's write our javascript file with baseline before see users parameter's methods.

conquest
  .Duration("2m")
  .Host("http://localhost:8080")
  .ConquestHeaders()
  .Headers({
    "X-Test": "True"
  })
  .Cookies({
    "debug": "true"
  })
  .Users(100, function(users){})
;

the most basic usage example looks like this. let's examine the users object's methods. the users object has only 4 methods:

defining transactions
users
  .Every(function(user){})
  .Then(function(user){})
  .Cases(function(user){})
  .Finally(function(user){})
;

the defined transactions which using Every method will be performed by every concurrent user before executing other transactions. And Finally transactions -as like as Every- will be performed by every user after all transactions. the defined transactions which are using Then or Cases methods will be performed randomly if you have choosen randomized mode. otherwise they will be performed in defined order. the Cases is an alias for Then.

the parameters of all three methods must be functions. that functions will be called by conquest such that passed user object. let's see methods of it.

users.Do(verb, path);

our main method. using this method, you can tell that perform verb for the path. let's call this method in our example:

conquest
  .Duration("2m")
  .Host("http://localhost:8080")
  .ConquestHeaders()
  .Headers({
    "X-Test": "True"
  })
  .Cookies({
    "debug": "true"
  })
  .Users(100, function(users){
    users
      .Every(function(user){

        /*
        * all users will be performed GET method for root path.
        */
        var t = user.Do("GET", "/");

      })
    ;
  })
;

Do method return an object which have some methods can manipulate request's headers, body, data and can query response's data etc.

t.Skip();

you can skip unnecessary transactions with using this method. conquest will not perform the transaction.

t.ClearHeaders();

this method clears the initial headers which defined via conquest.SetHeader and conquest.ConquestHeaders methods.

t.ClearCookies();

this method clears the initial cookies which defined via conquest.SetCookies method.

t.ClearInitials();

it clears both of initial cookies and headers.

t.RejectCookies();

using this method, you can reject cookies which sent by server, by this way, conquest will not store them.

t.SetCookie(cookies);

you can define some cookies will be able for this request. the cookies parameter must be an object such its keys must string but values can be strings or function (we will discuss later)

t.SetHeader(headers);

you can define some headers will be used for this request. the headers parameter must be an object such its keys must be string but values can be strings or function (we will discuss later)

t.Body(body);

you can define some cookies will be able for this transaction. the cookies parameter must be an object such its keys must string but values strings or function (we will discuss later)

it is time to meet fetch functions. when you assign a function for key in body/headers/cookies object for transaction, this function called and passed a parameter which it is a fetch object. with fetch object's methods, you can obtain some data from stored cookies or headers, and you can set form-data or upload files. see example:


/*
* upload new profile photo.
* we must provide xsrf form data.
*/
user
  .Do("POST", "/new_photo")
  .Body({
    "xsrf": function(fetch) { return fetch.FromCookie("xsrf"); },
    "file": function(fetch) { return fetch.FromDisk("test_files/photo.png"); }
  })
;

there is some restrictions about using fetch methods. you can use all methods for body but can not use FromDisk method for header or cookie values.

testing response

now we can perform some test about response. every transactions which created via Do method have Response object and the Response object have some methods for testing response.

t.Response.StatusCode(status_code);

it checks the equality between status code of response and the status_code parameter. the status_code parameter must be an integer.

t.Response.Header(header_name, header_value);

it checks the equality between value of header named header_name obtained from server and header_value.

t.Response.Cookie(cookie_name, cookie_value);

it is same as the Header method but it works for cookies.

t.Response.Contains(substr);

it searchs substr in response body.

see example for Response usage:


user
  .Do("POST", "/new_photo")
  .Body({
    "xsrf": function(fetch) { return fetch.FromCookie("xsrf"); },
    "file": function(fetch) { return fetch.FromDisk("test_files/photo.png"); }
  })
  .Response
    .StatusCode(200)
    .Contains("ok!")
;
        
full example

conquest
  .Duration("2m")
  .Host("http://localhost:8080")
  .ConquestHeaders()
  .Headers({
    "X-Test": "True"
  })
  .Cookies({
    "debug": "true"
  })
  .Users(100, function(users){
    users
      .Every(function(user){

        /*
        * all users will be performed GET method for root path.
        */
        user
          .Do("GET", "/")
        ;

        /*
        * log in
        */
        user
          .Do("POST", "/login")
          .Body({
            "xsrf": function(fetch){ return fetch.FromCookie("xsrf"); },
            "user": "root",
            "pass": "toor"
          })
          .Response
            .StatusCode(200)
        ;

      })
      .Then(function(user){
        /*
        * reach the profile page
        */
        user
          .Do("GET", "/profile")
          .Response
            .StatusCode(200)
        ;

        /*
        * upload new photo
        */
        user
          .Do("POST", "/new_photo")
          .Body({
            "xsrf": function(fetch) { return fetch.FromCookie("xsrf"); },
            "file": function(fetch) { return fetch.FromDisk("test_files/photo.png"); }
          })
          .Response
            .StatusCode(200)
            .Contains("ok!")
        ;
      })
      .Finally(function(user){
        /*
        * log out
        */
        user
          .Do("GET", "/logout")
          .Body({
            "sess": function(fetch) { return fetch.FromCookie("sess"); }
          })
          .Response
            .StatusCode(200)
        ;
      })
    ;
  })
;
        

or visit github repo for to see basic tornado application.