Testing Framework and Practices
Testing Frameworks and Toolchain
Deno Built-in Testing Framework
Deno natively supports testing without requiring additional frameworks, using the deno test command to run tests:
// math_test.ts
import { add, subtract } from "./math.ts";
import { assertEquals } from "https://deno.land/std@0.140.0/testing/asserts.ts";
Deno.test("add function", () => {
assertEquals(add(1, 2), 3); // Using built-in assertion
});
Deno.test("subtract function", () => {
assertEquals(subtract(5, 3), 2);
});
// Run: deno test math_test.tsAdvanced Testing Features
// Advanced testing example
import { assertExists } from "https://deno.land/std@0.140.0/testing/asserts.ts";
Deno.test({
name: "async operation test",
async fn() {
const data = await fetchData(); // Async operation
assertExists(data);
},
sanitizeResources: true, // Automatically clean up resources
sanitizeOps: true // Automatically clean up operations
});
// Setup and teardown hooks
Deno.test("setup and teardown", () => {
Deno.test({
name: "test with setup",
fn() { /* ... */ },
setup() { /* Initialization logic */ },
teardown() { /* Cleanup logic */ }
});
});Test Coverage and Reporting
Generating Coverage Reports
# Run tests and collect coverage
deno test --coverage=./coverage
# Generate HTML report
deno coverage ./coverage --lcov --output=lcov.info
genhtml lcov.info -o coverage_reportCI/CD Integration Example
# GitHub Actions example
name: Deno CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: denoland/setup-deno@v1
with:
deno-version: "1.35"
- run: deno test --coverage=./coverage
- run: deno coverage ./coverage --lcov --output=lcov.info
- uses: codecov/codecov-action@v3
with:
files: lcov.infoMocking and Dependency Injection
Manual Mocking of Dependencies
// Mocking HTTP request
import { assertEquals } from "https://deno.land/std@0.140.0/testing/asserts.ts";
Deno.test("fetch user data", async () => {
// Save original fetch
const originalFetch = globalThis.fetch;
// Mock fetch
globalThis.fetch = async () => {
return new Response(JSON.stringify({ id: 1, name: "Mock User" }), {
headers: { "Content-Type": "application/json" }
});
};
// Execute test
const user = await fetchUserData();
assertEquals(user.name, "Mock User");
// Restore original fetch
globalThis.fetch = originalFetch;
});Using Dependency Injection Pattern
// Testable code with dependency injection
import { assertEquals } from "https://deno.land/std@0.140.0/testing/asserts.ts";
interface HttpClient {
get(url: string): Promise<Response>;
}
class UserService {
constructor(private client: HttpClient) {}
async getUser(id: string): Promise<User> {
const resp = await this.client.get(`/users/${id}`);
return resp.json();
}
}
// Inject mock client during testing
Deno.test("UserService with mock", async () => {
const mockClient = {
get: async () => new Response(JSON.stringify({ id: "1", name: "Test" }))
};
const service = new UserService(mockClient);
const user = await service.getUser("1");
assertEquals(user.name, "Test");
});Build Tools and Optimization
Modern Build Process Design
Using Denox for Multi-Environment Management
// denox.json configuration example
{
"scripts": {
"dev": "deno run --allow-net src/server.ts",
"test": "deno test --coverage=./coverage",
"build": "deno bundle src/main.ts dist/bundle.js",
"start": "deno run --allow-net dist/bundle.js"
}
}
// Run scripts
denox run dev # Development mode
denox run build # Production buildCode Bundling and Optimization
Advanced Bundling Configuration
// Custom bundling with Deno.Bundle API
const bundled = await Deno.emit(
"https://deno.land/x/oak@v12.6.1/mod.ts", // Entry file
{
bundle: "module",
compilerOptions: {
// Compilation options
lib: ["dom", "esnext"],
target: "es2020"
}
}
);
// Write to file
await Deno.writeTextFile("bundle.js", bundled.files["deno:///bundle.js"]);
// Production optimization recommendations:
// 1. Use --no-check to skip type checking (when pre-checked)
// 2. Specify --import-map to reduce repeated parsing
// 3. Enable --v8-flags=--turbofan for V8 execution optimizationTree-Shaking Practice
// Module design example (enabling tree-shaking)
// utils/math.ts
export function add(a: number, b: number) { /* ... */ }
export function subtract(a: number, b: number) { /* ... */ }
// Main entry file
import { add } from "./utils/math.ts"; // Import only add function
// Unused subtract function will be automatically removed during bundlingPerformance Optimization Strategies
Caching and Incremental Builds
# Utilize Deno caching mechanism
deno cache --import-map=import_map.json src/deps.ts
# Incremental build script example
#!/bin/bash
changed_files=$(git diff --name-only HEAD^ HEAD | grep '\.ts$')
if [ -n "$changed_files" ]; then
deno cache $changed_files
deno bundle src/main.ts dist/bundle.js
fiProduction Environment Optimization Checklist
- Use
--no-checkto skip type checking (ensure pre-checked withdeno check). - Enable
--import-mapto reduce module resolution overhead. - Configure V8 optimization flags:
--v8-flags=--turbofan --v8-flags=--no-lazy. - Distribute static assets via CDN.
- Enable HTTP/2 server push.
Workflow Automation
Development Environment Configuration
Recommended Toolchain Combination
// .vscode/settings.json
{
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "denoland.vscode-deno"
}Development Server Configuration
// Development middleware with Oak
import { Application } from "https://deno.land/x/oak@v12.6.1/mod.ts";
const app = new Application();
// Development environment middleware
if (Deno.env.get("MODE") === "development") {
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
}
// Hot reload implementation (requires tools like denon)
app.use(async (ctx) => {
ctx.response.body = "Hello World";
});
await app.listen({ port: 8000 });Continuous Integration Solutions
Complete GitHub Actions Example
name: Deno CI Pipeline
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: denoland/setup-deno@v1
with:
deno-version: "1.35"
# Install dependencies
- run: deno cache --import-map=import_map.json src/deps.ts
# Run tests
- run: deno test --allow-net --allow-read
# Code quality checks
- run: deno lint
- run: deno fmt --check
# Generate coverage
- run: deno test --coverage=./coverage
- run: deno coverage ./coverage --lcov --output=lcov.info
- uses: codecov/codecov-action@v3
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: denoland/setup-deno@v1
# Build production version
- run: deno bundle src/main.ts dist/bundle.js
# Deploy to server (example)
- uses: appleboy/scp-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
source: "dist/"
target: "/var/www/myapp"Deployment Strategies and Practices
Containerized Deployment Solution
# Dockerfile example
FROM denoland/deno:1.35
# Install dependencies (leverage Deno cache layer)
WORKDIR /app
COPY import_map.json .
RUN deno cache --import-map=import_map.json src/deps.ts
# Copy source code
COPY . .
# Build production version
RUN deno bundle src/main.ts dist/bundle.js
# Run application (non-root user)
USER deno
EXPOSE 8000
CMD ["run", "--allow-net", "--allow-read", "dist/bundle.js"]Serverless Deployment Example
# serverless.yml configuration
service: deno-serverless
provider:
name: aws
runtime: provided.al2
environment:
DENO_DIR: /tmp/deno_dir
functions:
hello:
handler: handler.hello
events:
- http:
path: /hello
method: get
plugins:
- serverless-deno-plugin
custom:
deno:
version: "1.35"
importMap: import_map.json
layers:
- arn:aws:lambda:us-east-1:123456789012:layer:deno:1Advanced Workflow Patterns
Microservices Development Pattern
Service Communication Testing Solution
// Testing microservice communication
import { assertEquals, assertMatch } from "https://deno.land/std@0.140.0/testing/asserts.ts";
Deno.test("order service integration", async () => {
// Start payment service mock
const paymentServer = Deno.run({
cmd: ["deno", "run", "--allow-net", "payment_service_mock.ts"],
stdout: "piped"
});
try {
// Test order service
const orderResponse = await fetch("http://localhost:8000/orders", {
method: "POST",
body: JSON.stringify({ productId: 1 })
});
assertEquals(orderResponse.status, 201);
// Verify payment service call
const paymentLogs = new TextDecoder().decode(
await paymentServer.output()
);
assertMatch(paymentLogs, /Payment processed/);
} finally {
paymentServer.close();
}
});Frontend Engineering Integration
Deno with Modern Frontend Toolchain
// vite.config.js (integrating Deno type checking)
import { defineConfig } from 'vite';
import denoPlugin from 'vite-plugin-deno';
export default defineConfig({
plugins: [
denoPlugin({
// Specify Deno configuration file
config: './deno.json',
// Enable type checking
check: true,
// Specify import map
importMap: './import_map.json'
})
]
});Shared Type Definitions Solution
// shared_types.d.ts
declare interface User {
id: string;
name: string;
email: string;
}
// Share this type definition between Deno and frontend
// In Deno:
// /// <reference path="./shared_types.d.ts" />
// In frontend project, import directlyMulti-Environment Configuration Management
Dynamic Environment Configuration Implementation
// config.ts
type Env = "development" | "production" | "test";
const env = Deno.env.get("DENO_ENV") as Env || "development";
const configs: Record<Env, {
dbUrl: string;
apiPort: number;
}> = {
development: {
dbUrl: "postgres://dev_user@localhost:5432/dev_db",
apiPort: 8000
},
production: {
dbUrl: Deno.env.get("DB_URL") || "",
apiPort: 80
},
test: {
dbUrl: "postgres://test_user@localhost:5432/test_db",
apiPort: 8001
}
};
export default configs[env];Environment Variable Validation Solution
// env_validator.ts
const requiredVars = {
development: ["DB_URL", "API_KEY"],
production: ["DB_URL", "API_KEY", "SECRET"],
test: ["DB_URL"]
};
function validateEnv() {
const env = Deno.env.get("DENO_ENV") as string;
const missing = requiredVars[env].filter(v => !Deno.env.get(v));
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(", ")}`);
}
}
validateEnv();



