Skip to content

Testing

Unit and integration testing strategies for NestForge applications.

Automated testing is a critical part of software development. NestForge provides a set of testing utilities that allow for efficient unit testing and robust integration testing by leveraging a testing module system.

The TestFactory is the primary entry point for creating a testing environment. It allows you to create a mock-vibe of your application where you can override providers (like databases or third-party services) before building the container.

#[tokio::test]
async fn test_users_service() {
let module = nestforge::TestFactory::<AppModule>::create()
.override_provider(MockDatabase::default())
.build()
.expect("Failed to build testing module");
let service = module.resolve::<UsersService>().expect("Could not resolve service");
let user = service.find_by_id(1).await;
assert!(user.is_ok());
}

Integration tests allow you to test the entire request/response cycle, including routing, validation, and middleware.

NestForge’s TestingModule can produce an http_router() which is a standard Axum router. You can use the tower::ServiceExt trait to send simulated requests to it.

use tower::ServiceExt; // for .oneshot()
use axum::http::{Request, StatusCode};
use axum::body::Body;
#[tokio::test]
async fn test_get_users_route() {
let module = nestforge::TestFactory::<AppModule>::create().build().unwrap();
let app = module.http_router();
let response = app
.oneshot(
Request::builder()
.uri("/users")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}

One of the most powerful features of the NestForge testing system is the ability to swap out actual implementations with mocks or stubs.

let module = nestforge::TestFactory::<AppModule>::create()
// Replace the real MailService with a mock that doesn't send real emails
.override_provider(MockMailService::new())
.build()
.unwrap();

The testing module isn’t limited to HTTP. You can create specialized contexts for any transport:

  • GraphQL: module.graphql_router(schema)
  • WebSockets: module.websocket_context()
  • gRPC: module.grpc_context()

This ensures that your transport-specific logic (like gRPC interceptors or WebSocket auth) can be tested in isolation but with full DI support.


  1. Use oneshot for HTTP Tests: It’s faster than starting a real TCP server and sufficient for 99% of integration tests.
  2. Clean up with shutdown(): If your modules use lifecycle hooks (like closing DB pools), call module.shutdown() at the end of your test.
  3. Mock Externally: Always override providers that make external network calls (Stripe, Twilio, AWS) to keep your tests fast and deterministic.