7.4 KiB
Testing Guide for Zitadel Configurator
This document explains the testing approach and methodology used in the Zitadel Configurator project, which uses CDKTF (Cloud Development Kit for Terraform) to manage Zitadel infrastructure.
Overview
The project uses unit testing with Jest to test Infrastructure as Code (IaC) definitions. Tests verify that the synthesized Terraform configuration contains the expected resources and properties without creating actual cloud resources.
Testing Framework
Core Technologies
- Jest: JavaScript testing framework
- ts-jest: TypeScript support for Jest
- CDKTF Testing: Built-in testing utilities for CDKTF applications
- TypeScript: Primary language for both implementation and tests
Configuration Files
jest.config.js
: Jest configuration with TypeScript supportsetup.js
: CDKTF-specific Jest setup that enables testing matcherspackage.json
: Test scripts and dependencies
Test Structure
Directory Organization
zitadel-configurator/
├── __tests__/ # Test files directory
│ └── main.test.ts # Main test suite
├── main.ts # Implementation to be tested
├── jest.config.js # Jest configuration
└── setup.js # CDKTF Jest setup
Test File Naming
- Tests are located in the
__tests__/
directory - Test files follow the pattern:
*.test.ts
- Alternative patterns supported:
*.spec.ts
Running Tests
Available Commands
# Run all tests once
npm test
# Run tests in watch mode (auto-rerun on file changes)
npm run test:watch
Test Output
Tests provide clear feedback about:
- Resource creation verification
- Property validation
- Synthesized Terraform configuration structure
Testing Approach
Unit Testing Philosophy
The project follows a unit testing approach where:
- Tests validate the synthesized Terraform configuration
- No actual cloud resources are created during testing
- Fast execution with immediate feedback
- Focus on infrastructure definition correctness
Test Categories
1. Resource Existence Tests
Verify that expected resources are created in the Terraform configuration:
it("should create an organization resource", () => {
const app = Testing.app();
const stack = new ZitadelStack(app, "test-stack");
// Test that the stack contains an Org resource
expect(Testing.synth(stack)).toHaveResource(Org);
});
2. Resource Property Tests
Validate that resources have the correct properties:
it("should create organization with name 'makers'", () => {
const app = Testing.app();
const stack = new ZitadelStack(app, "test-stack");
// Test the synthesized terraform to ensure it contains the expected resource properties
expect(Testing.synth(stack)).toHaveResourceWithProperties(Org, {
name: "makers"
});
});
3. Public Interface Tests
Verify that the stack exposes the expected outputs:
it("should create organization with name 'makers'", () => {
const app = Testing.app();
const stack = new ZitadelStack(app, "test-stack");
// Verify the organization was created and stored
expect(stack.createdOrg).toBeDefined();
});
CDKTF Testing Utilities
Key Testing Methods
Testing.app()
Creates a CDKTF app instance for testing purposes.
Testing.synth(stack)
Synthesizes the stack into Terraform JSON configuration for testing.
toHaveResource(ResourceClass)
Custom Jest matcher that checks if the synthesized configuration contains a specific resource type.
toHaveResourceWithProperties(ResourceClass, properties)
Custom Jest matcher that validates both resource existence and specific property values.
Setup Requirements
The setup.js
file is crucial as it:
const cdktf = require("cdktf");
cdktf.Testing.setupJest();
This enables CDKTF-specific Jest matchers and testing utilities.
Best Practices
1. Test Structure
- Use descriptive test suite names (
describe
blocks) - Write clear, specific test case descriptions
- Group related tests logically
2. Test Isolation
- Each test creates its own app and stack instance
- Tests are independent and don't share state
- Use fresh instances for each test case
3. Comprehensive Coverage
- Test resource creation
- Validate resource properties
- Verify public interfaces and outputs
- Test different configuration scenarios
4. Meaningful Assertions
- Test both existence and correctness
- Use specific matchers for clear error messages
- Validate the actual synthesized configuration
Example Test Implementation
import "cdktf/lib/testing/adapters/jest"; // Load types for expect matchers
import { Testing } from "cdktf";
import { ZitadelStack } from "../main";
import { Org } from "../.gen/providers/zitadel/org";
describe("Zitadel Configurator", () => {
describe("Unit testing using assertions", () => {
it("should create an organization resource", () => {
const app = Testing.app();
const stack = new ZitadelStack(app, "test-stack");
// Test that the stack contains an Org resource
expect(Testing.synth(stack)).toHaveResource(Org);
});
it("should create organization with name 'makers'", () => {
const app = Testing.app();
const stack = new ZitadelStack(app, "test-stack");
// Verify the organization was created and stored
expect(stack.createdOrg).toBeDefined();
// Test the synthesized terraform to ensure it contains the expected resource properties
expect(Testing.synth(stack)).toHaveResourceWithProperties(Org, {
name: "makers"
});
});
});
});
Benefits of This Testing Approach
1. Fast Feedback
- Tests run quickly without cloud API calls
- Immediate validation of infrastructure definitions
- Suitable for continuous integration
2. Cost-Effective
- No cloud resources created during testing
- No API rate limits or costs
- Safe for frequent execution
3. Reliable
- Tests are deterministic and repeatable
- No dependency on external services during testing
- Consistent results across different environments
4. Development-Friendly
- Supports test-driven development (TDD)
- Watch mode for rapid iteration
- Clear error messages for debugging
Extending Tests
Adding New Test Cases
When adding new resources or modifying existing ones:
- Test Resource Creation: Verify the new resource type exists
- Test Properties: Validate all important configuration properties
- Test Relationships: Verify resource dependencies and references
- Test Public Interface: Ensure any exposed outputs are accessible
Advanced Testing Scenarios
Consider adding tests for:
- Error conditions and validation
- Different configuration environments
- Resource dependencies and relationships
- Complex property calculations
Troubleshooting
Common Issues
Missing CDKTF Setup
If you see errors about missing matchers, ensure setup.js
is properly configured in jest.config.js
.
TypeScript Compilation Errors
Ensure all necessary type definitions are imported:
import "cdktf/lib/testing/adapters/jest"; // Load types for expect matchers
Resource Import Issues
Verify that generated provider resources are properly imported:
import { Org } from "../.gen/providers/zitadel/org";
This testing approach ensures robust, reliable infrastructure definitions while maintaining development velocity and cost-effectiveness.