Testing
Cot includes various built-in utilities to help you test your application. This guide will cover some of the most important ones.
Why Test at All?
Testing is a critical part of any application development process. By writing and running tests, you can:
- Ensure Code Reliability – Tests catch bugs and regressions before they reach production, increasing overall stability and confidence in your application.
- Document Your Code – Tests serve as living documentation. They show how different parts of your application are supposed to work and can act as examples for future maintainers.
- Facilitate Refactoring – With a robust test suite, you can safely modify or refactor your code. If something breaks, your tests will let you know right away.
- Encourage Good Design – When code is easier to test, it often means it’s well-structured and follows good design principles.
By employing Cot’s testing utilities, you’ll be able to verify that each piece of your application—from individual request handlers to full end-to-end processes—works correctly.
General Overview
Cot provides several built-in utilities located in the cot::test module to help you create and run tests for your application.
Typical Rust projects keep their tests in:
- A dedicated
tests/directory (for integration tests). - A
mod testssection in your source files (for unit tests).
You can run all your tests by executing:
Unit Testing
Unit tests focus on testing small, isolated pieces of your application, such as individual functions, request handlers, or utility methods. Cot’s TestRequestBuilder utility helps you create HTTP request objects in a lightweight way, without spinning up a full HTTP server.
Test Request Builder
The TestRequestBuilder offers a fluent API for constructing HTTP requests that can be dispatched to your request handlers directly:
// Create a GET request
let request = ;
// Create a POST request
let request = ;
// Add configuration and features
let request =
// Add default configuration
// Add session support
;
// Add form data
let request =
;
// Add JSON data
let request =
;
When to Use TestRequestBuilder
- Handler Testing: Verify that individual handlers behave correctly given different inputs (e.g., form data, JSON bodies).
- Config-dependent Testing: Make sure your handlers behave as expected when certain configurations or features (like sessions) are enabled.
Integration Testing
Integration tests check how multiple parts of your application work together. Cot provides a Client struct to help you simulate end-to-end HTTP interactions with a fully running instance of your application.
Test Client
The Client struct lets you create a temporary instance of your Cot application and perform HTTP requests against it:
let project =
?;
// Create a new test client
let mut client = ;
// Make GET requests
let response = ?;
// Make custom requests
let request = ?;
let response = ?;
When to Use Client
- Full Application Testing: Confirm that routes, middlewares, and database integrations all work as intended.
- Multi-Request Sequences: Test flows that require multiple requests, like login/logout or multi-step forms.
Test Database
Cot’s testing utilities also include the TestDatabase struct, which helps you create temporary databases for your tests. This allows you to test how your application interacts with data storage without polluting your real database.
// Create SQLite test database (in-memory)
let mut test_db = ?;
// Create PostgreSQL test database
let mut test_db = ?;
// Create MySQL test database
let mut test_db = ?;
// Use the test database in requests
let request =
;
// Add authentication support
;
// Clean up after testing
?;
Best Practices
-
Always Clean Up Test Databases
let test_db = ?; // ... run your tests ... ?;Cleaning up helps ensure that each test runs in isolation and that temporary databases don’t accumulate. Note that if a test panics, the database will not be cleaned up, which might be useful for debugging. On the next test run, the database will be removed automatically.
-
Use Unique Test Names for PostgreSQL/MySQL
let test_db = ?;This prevents naming collisions when running multiple tests or suites simultaneously.
-
Add Migrations and Auth Support If Required
let mut test_db = ?; ; let request = ;This ensures that your tests have all necessary schema and authentication information set up.
Environment Variables
-
POSTGRES_URL
The connection URL for PostgreSQL (default:postgresql://cot:cot@localhost). -
MYSQL_URL
The connection URL for MySQL (default:mysql://root:@localhost).
Important Notes
- PostgreSQL and MySQL test databases are created with the prefix
test_cot__. - The SQLite database is in-memory by default.
- Form data is currently only supported with POST requests.
- Custom migrations can be added using the
add_migrationsmethod onTestDatabase.
Test Cache
Cot’s testing utilities also include the TestCache struct, which helps you create temporary caches for your tests. This allows you to test how your application interacts with the cache without polluting your real cache.
use ;
// Create a memory cache
let test_cache = ;
let cache = ;
// Or Redis (requires `redis` feature and a running Redis instance)
let test_cache = ?;
let cache = ;
// Use the cache in requests
let request =
;
// Clean up after testing (important for Redis)
?;
The Redis test cache uses a randomized key prefix to ensure isolation between tests.
End-to-end testing
Cot provides an end-to-end testing framework that allows you to test your entire application in a near-production environment. This is particularly useful for testing complex workflows that involve multiple components, such as user authentication, database interactions, external API calls, and your application’s UI. By using the end-to-end testing framework you will be able to send real HTTP requests or use web automation tools to simulate user interactions with your application.
The end-to-end testing framework consists of two parts: the cot::e2e_test macro and the TestServerBuilder struct. The cot::e2e_test macro allows you to define end-to-end tests that allow you to run your project in the background, while the TestServerBuilder struct allows you to create a test server that you can send your requests to. An example of how to use the cot::e2e_test macro and the TestServerBuilder struct is shown below in a simple test that checks if the server is running and returns the Hello world! response:
;
async
The test above uses reqwest, a popular HTTP client for Rust, to send a GET request to the test server.
Cot’s end-to-end test framework is also particularly useful when using web automation tools that test your web application using an actual web browser. Some popular crates for web automation in Rust include:
Please refer to the documentation of these crates for more information on how to use them.
Summary
Cot’s testing framework provides a robust and flexible approach to ensuring the quality of your application.
- Unit tests with
TestRequestBuilderhelp you verify that individual components behave as expected. - Integration tests with
Clientlet you test your entire application in a near-production environment, whileTestDatabaseandTestCachegive you confidence that your data and caching layers are functioning correctly. - End-to-end tests with
TestServerBuilderallow you to verify your full application workflows in a real-world scenario.
By integrating these testing tools into your workflow, you can deploy your Cot applications with greater confidence. Happy testing!