Skip to main content

Low Level API

Previously, we covered Touca Main API to test a simple software workflow using Touca's built-in test runner.

import touca
import students as code_under_test

@touca.workflow
def students(username: str):
student = code_under_test.find_student(username)
# insert code here to describe the behavior
# and performance of the code under test

if __name__ == "__main__":
touca.run()

workflow and run are the entry-points to Touca's built-in test runner. In addition to running our code under test with different test cases, the test runner provides facilities that include reporting progress, handling errors, parsing command line arguments, and many more. We intentionally designed this API to abstract away these common features to let developers focus on their code under test.

Touca SDKs provide a separate low-level API that offers more flexibility and control over how tests are executed and how their results are handled. This API is most useful when integrating Touca with other test frameworks.

import touca
from students import find_student

if __name__ == "__main__":
touca.configure(
api_key="<TOUCA_API_KEY>",
api_url="<TOUCA_API_URL>",
)
for username in ["alice", "bob", "charlie"]:
touca.declare_testcase(username)

student = find_student(username)
# insert code here to describe the behavior
# and performance of the workflow under test

touca.post()
touca.save_json(f"touca_{username}.json")
touca.save_binary(f"touca_{username}.bin")
touca.forget_testcase(username)

touca.seal()

The above code uses the low-level Touca API to perform the same operations as the Touca test runner without handling errors, reporting progress, and handling command line arguments. In this section, we will review the functions used in this code and explain what they do.

Configuring the Client

Touca client requires a one-time call of function configure. This configuration effectively activates all other Touca functions for capturing data and submission of results. Therefore, this function must be called from our test tool, and not from our code under test. This design enables us to leave the calls to Touca data capturing functions in our production code without having to worry about their performance impact.

The configure function can take various configuration parameters including the Touca API Key and API URL. Refer to the Reference API documentation of your SDK for the full list of supported configuration parameters and their impact.

touca.configure(
api_key="<TOUCA_API_KEY>",
api_url="<TOUCA_API_URL>",
revision="<TOUCA_TEST_VERSION>"
)

Touca API Key should be treated as a secret. We advise against hard-coding this parameter.

The three common parameters, API Key, API URL, and version of the code under test can also be set as environment variables TOUCA_API_KEY, TOUCA_API_URL, and TOUCA_TEST_VERSION. Environment variables always override the parameters passed to the configure function.

All of the configuration parameters passed to configure are optional. When API Key and API URL are missing, the client is configured in the offline mode. It can still capture data and store them to files but it will not submit them to the Touca server.

You can always force the client to run in offline mode by passing the offline parameter to the configure function.

Declaring Test Cases

Once the client is configured, you can call declare a test case to indicate that all subsequent calls to the data capturing functions like check should associate the captured data with that declared test case.

for username in ["alice", "bob", "charlie"]:
touca.declare_testcase(username)
# now we can start calling our code under test
# and describing its behavior and performance

Test cases are unique names that identify different inputs to our code under test. These inputs can be anything as long as they are expected to produce the same behavior every time our code is executed.

Submitting Test Results

Once we execute our code under test for each test case and describe its behavior and performance, we can submit them to the Touca server.

touca.post()

The server stores the captured data, compares them against the submitted data for pervious versions of our code, visualizes any differences, and reports them in real-time.

It is possible to call this function multiple times during the runtime of our test tool. Test cases already submitted to the Touca server whose results have not changed, will not be resubmitted. It is also possible to add new results for an already submitted test case. Any subsequent call to the function will resubmit the modified test cases.

We generally recommend that you post test results after running the code under test for each test case. This practice ensures real-time feedback about the test results, as they are executed.

Storing Test Results

If we like to do so, we can store our captured data for one or more declared test cases on the local filesystem for further processing or later submission to the Touca server.

touca.save_binary(f"touca_${username}.bin")
touca.save_json(f"touca_${username}.json")

We can store captured data in JSON or binary format. While JSON files are preferable for quick inspections, only binary files may be posted to the Touca server at a later time.

Forgetting Test Cases

You could ask the Touca client to forget a given test case after submission of its data to the server. This operation is useful in tests that collect significant amount of data for a large number of test cases.

touca.forget_testcase()

Sealing Test Results

When all the test cases are executed for a given version of our code under test, we have the option to seal the version to let the server know that no further test result is expected to be submitted for it. This allows the server to send the final comparison result report to interested users, as soon as it is available.

touca.seal()

Sealing the version is optional. The Touca server automatically performs this operation once a certain amount of time has passed since the last test case was submitted.