lemoncheesecake-requests

lemoncheesecake-requests provides logging facilities to requests for tests written with the lemoncheesecake test framework.

In this example, we implement a very basic test on a Github API endpoint:

# suites/github.py

import lemoncheesecake.api as lcc
from lemoncheesecake.matching import *
from lemoncheesecake_requests import Session, is_2xx

@lcc.test()
def get_org():
    session = Session(base_url="https://api.github.com")

    resp = session.get("/orgs/lemoncheesecake"). \
        require_status_code(is_2xx()). \
        check_json({
             "id": is_integer(),
             "name": equal_to("lemoncheesecake")
        })

We run the test:

$ lcc run
=================================== github ====================================
 OK  1 # github.get_org

Statistics :
 * Duration: 0.214s
 * Tests: 1
 * Successes: 1 (100%)
 * Failures: 0

And here are the report details :

_images/report-sample.png

Installation

Install through pip:

$ pip install lemoncheesecake-requests

lemoncheesecake-requests is compatible with Python 3.7-3.10.

Features

Session

lemoncheesecake-requests extends requests.Session to add logging features through a lemoncheesecake_requests.Logger instance.

This logger which can be set session-wide (it is set to Logger.on() by default):

session = Session(base_url="https://api.github.com", logger=Logger.no_headers())
session.get("/orgs/lemoncheesecake")

or overridden on a per request basis:

session = Session(base_url="https://api.github.com")
session.get("/orgs/lemoncheesecake", logger=Logger.no_headers())

The base_url argument is optional and an extra hint argument is also available to provide more context in the logs to the report reader.

As you might guess from the previous examples, the logger can be fine tuned to control exactly what HTTP request/response details that will be logged and how through the following logger boolean attributes:

If you want for instance to create a logger that only logs data coming from the response:

logger = Logger(
   request_line_logging=False
   request_headers_logging=False
   request_body_logging=False
)

and then pass this logger to a session or to a specific HTTP method call.

The boolean debug attribute controls whether or not logging is done using the “debug” level or the default “info” level. Please note that with lemoncheesecake 1.10.0 and higher debug logs are hidden by default in HTML reports and lcc report (with an option to show them) which make them convenient to log less-important data without overloading the report.

The lemoncheesecake_requests.Logger class also provide class methods to easily create instances for common usage cases:

HTTP request bodies and especially response bodies might be very large and make the final report unreadable. That’s why the logger will log the request/response bodies as attachment if their (serialized) content size exceed a certain size. This size can be configured through the max_inlined_body_size logger attribute.

Response

The various request-methods (get(), post(), etc…) of lemoncheesecake_requests.Session return a lemoncheesecake_requests.Response which extends requests.Response and provide several extra methods to check various aspect of the response such as its status code, headers and JSON content.

As you may already know, lemoncheesecake provides three different ways (for three different behaviors) to perform a matching operation:

The lemoncheesecake_requests.Response follows the same logic by offering various check_, require_ and assert_ methods.

Status code

The following methods are available:

Where expected can be either an int or a Matcher instance, so that the following statements are all valid:

resp.check_status_code(200)
resp.check_status_code(equal_to(200))
resp.check_status_code(any_of(200, 201))

lemoncheesecake-requests provides the is_2xx(), is_3xx(), is_4xx(), is_5xx() matchers to check status code family:

resp.check_status_code(is_2xx())

There is also an alternative way to check status code: Response.raise_unless_status_code(expected) will directly raise a StatusCodeMismatch exception if the condition is not met and then interrupt the test (unless the exception is explicitly caught):

resp.raise_unless_status_code(200)

(this function can also take a Matcher instance as argument).

All these methods have a corresponding shortcut method that directly check a 2xx status code:

It means that those two method calls are equivalent:

resp.check_status_code(is_2xx())
resp.check_ok()

Response headers

Response headers can be checked with:

resp.check_header("Content-Type", "application/json")

or:

resp.check_headers({"Content-Type": "application/json"})

The expected value (i.e “application/json” in this example) can also be a matcher instance.

Like status code check, these two methods exist with their require_ and assert_ counterparts.

Response JSON

Response JSON can be checked with:

resp.check_json({"id": is_integer()})

Like status code check, this method exists with its require_ and assert_ counterparts.

Notes

Please note that all these extra methods return the Response instance itself, meaning that they can be chained like this:

resp = session.get("/orgs/lemoncheesecake").require_ok()

See the API Reference for full details about the lemoncheesecake-requests API.

Changelog

The Changelog will tell you about features, improvements and fixes of each version.