We use cookies to enhance your experience on the site
CodeWorlds

Test Automation - Automatic Quality Controls

Master of automation! Consul Caesar.js discovered that the best forts are those with automated systems controlling every aspect of the campaign. Test Automation is a magic machine that watches over code quality 24/7, even while you sleep!

What Is Test Automation?

Test Automation is like automated fort systems:

  • Automatic alarms - CI/CD pipelines
  • System self-checks - automated test suites
  • Regular inspections - scheduled test runs
  • Instant notifications - instant feedback
  • Disaster prevention - preventing bad code from being deployed
1# Example - automatic quality control pipeline
2name: Legionary Cohort Quality Control
3
4on:
5 push:
6 branches: [main, develop]
7 pull_request:
8 branches: [main]
9
10jobs:
11 quality-control:
12 runs-on: ubuntu-latest
13 steps:
14 - name: Check out the cohort
15 uses: actions/checkout@v3
16
17 - name: Setup Node.js
18 uses: actions/setup-node@v3
19 with:
20 node-version: '18'
21
22 - name: Load cargo (dependencies)
23 run: npm ci
24
25 - name: Lint check (inspect the cohort)
26 run: npm run lint
27
28 - name: Run all tests
29 run: npm run test:cov
30
31 - name: Build the cohort
32 run: npm run build

CI/CD Pipeline Setup for NestJS

GitHub Actions Configuration

1# .github/workflows/ci.yml
2name: Continuous Integration
3
4on:
5 push:
6 branches: [main, develop, 'feature/*']
7 pull_request:
8 branches: [main, develop]
9
10env:
11 NODE_VERSION: '18'
12 POSTGRES_DB: legionaries_test_db
13 POSTGRES_USER: test_user
14 POSTGRES_PASSWORD: test_password
15
16jobs:
17 # Job 1: Code Quality Checks
18 code-quality:
19 name: Code Quality
20 runs-on: ubuntu-latest
21
22 steps:
23 - name: Checkout repository
24 uses: actions/checkout@v3
25
26 - name: Setup Node.js
27 uses: actions/setup-node@v3
28 with:
29 node-version: ${{ env.NODE_VERSION }}
30 cache: 'npm'
31
32 - name: Install dependencies
33 run: npm ci
34
35 - name: Run ESLint
36 run: npm run lint
37
38 - name: Run Prettier check
39 run: npm run format:check
40
41 - name: TypeScript type check
42 run: npm run type-check
43
44 - name: Check for security vulnerabilities
45 run: npm audit --audit-level high
46
47 # Job 2: Unit Tests
48 unit-tests:
49 name: Unit Tests
50 runs-on: ubuntu-latest
51 needs: code-quality
52
53 steps:
54 - name: Checkout repository
55 uses: actions/checkout@v3
56
57 - name: Setup Node.js
58 uses: actions/setup-node@v3
59 with:
60 node-version: ${{ env.NODE_VERSION }}
61 cache: 'npm'
62
63 - name: Install dependencies
64 run: npm ci
65
66 - name: Run unit tests with coverage
67 run: npm run test:cov
68
69 - name: Upload coverage to Codecov
70 uses: codecov/codecov-action@v3
71 with:
72 file: ./coverage/lcov.info
73 flags: unittests
74 name: codecov-umbrella
75
76 - name: Coverage Comment
77 uses: romeovs/lcov-reporter-action@v0.3.1
78 with:
79 github-token: ${{ secrets.GITHUB_TOKEN }}
80 lcov-file: ./coverage/lcov.info
81 delete-old-comments: true
82
83 # Job 3: Integration Tests
84 integration-tests:
85 name: Integration Tests
86 runs-on: ubuntu-latest
87 needs: unit-tests
88
89 services:
90 postgres:
91 image: postgres:13
92 env:
93 POSTGRES_DB: ${{ env.POSTGRES_DB }}
94 POSTGRES_USER: ${{ env.POSTGRES_USER }}
95 POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
96 options: >-
97 --health-cmd pg_isready
98 --health-interval 10s
99 --health-timeout 5s
100 --health-retries 5
101 ports:
102 - 5432:5432
103
104 redis:
105 image: redis:6
106 options: >-
107 --health-cmd "redis-cli ping"
108 --health-interval 10s
109 --health-timeout 5s
110 --health-retries 5
111 ports:
112 - 6379:6379
113
114 steps:
115 - name: Checkout repository
116 uses: actions/checkout@v3
117
118 - name: Setup Node.js
119 uses: actions/setup-node@v3
120 with:
121 node-version: ${{ env.NODE_VERSION }}
122 cache: 'npm'
123
124 - name: Install dependencies
125 run: npm ci
126
127 - name: Run database migrations
128 run: npm run migration:run
129 env:
130 DATABASE_URL: postgres://test_user:test_password@localhost:5432/legionaries_test_db
131
132 - name: Run integration tests
133 run: npm run test:integration
134 env:
135 DATABASE_URL: postgres://test_user:test_password@localhost:5432/legionaries_test_db
136 REDIS_URL: redis://localhost:6379
137
138 # Job 4: E2E Tests
139 e2e-tests:
140 name: E2E Tests
141 runs-on: ubuntu-latest
142 needs: integration-tests
143
144 services:
145 postgres:
146 image: postgres:13
147 env:
148 POSTGRES_DB: ${{ env.POSTGRES_DB }}
149 POSTGRES_USER: ${{ env.POSTGRES_USER }}
150 POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
151 options: >-
152 --health-cmd pg_isready
153 --health-interval 10s
154 --health-timeout 5s
155 --health-retries 5
156 ports:
157 - 5432:5432
158
159 steps:
160 - name: Checkout repository
161 uses: actions/checkout@v3
162
163 - name: Setup Node.js
164 uses: actions/setup-node@v3
165 with:
166 node-version: ${{ env.NODE_VERSION }}
167 cache: 'npm'
168
169 - name: Install dependencies
170 run: npm ci
171
172 - name: Build application
173 run: npm run build
174
175 - name: Start application
176 run: |
177 npm run start:prod &
178 sleep 10
179 env:
180 DATABASE_URL: postgres://test_user:test_password@localhost:5432/legionaries_test_db
181
182 - name: Run E2E tests
183 run: npm run test:e2e
184
185 - name: Upload E2E test artifacts
186 if: failure()
187 uses: actions/upload-artifact@v3
188 with:
189 name: e2e-test-results
190 path: |
191 test-results/
192 screenshots/
193
194 # Job 5: Security Scan
195 security-scan:
196 name: Security Scan
197 runs-on: ubuntu-latest
198 needs: code-quality
199
200 steps:
201 - name: Checkout repository
202 uses: actions/checkout@v3
203
204 - name: Run Snyk to check for vulnerabilities
205 uses: snyk/actions/node@master
206 env:
207 SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
208 with:
209 args: --severity-threshold=high
210
211 - name: Upload Snyk results to GitHub Code Scanning
212 uses: github/codeql-action/upload-sarif@v2
213 if: always()
214 with:
215 sarif_file: snyk.sarif
216
217 # Job 6: Performance Tests
218 performance-tests:
219 name: Performance Tests
220 runs-on: ubuntu-latest
221 needs: integration-tests
222 if: github.event_name == 'push' && github.ref == 'refs/heads/main'
223
224 steps:
225 - name: Checkout repository
226 uses: actions/checkout@v3
227
228 - name: Setup Node.js
229 uses: actions/setup-node@v3
230 with:
231 node-version: ${{ env.NODE_VERSION }}
232 cache: 'npm'
233
234 - name: Install dependencies
235 run: npm ci
236
237 - name: Run performance tests
238 run: npm run test:performance
239
240 - name: Upload performance results
241 uses: actions/upload-artifact@v3
242 with:
243 name: performance-results
244 path: performance-results.json

Pre-commit Hooks - Pre-Loading Inspection

Husky + lint-staged Setup

1// package.json
2{
3 "scripts": {
4 "prepare": "husky install",
5 "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
6 "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
7 "format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
8 "type-check": "tsc --noEmit"
9 },
10 "lint-staged": {
11 "*.{ts,js}": [
12 "eslint --fix",
13 "prettier --write",
14 "git add"
15 ],
16 "*.ts": [
17 "npm run type-check"
18 ]
19 },
20 "husky": {
21 "hooks": {
22 "pre-commit": "lint-staged",
23 "pre-push": "npm run test:unit",
24 "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
25 }
26 }
27}

Custom pre-commit script

1#!/bin/sh
2# .husky/pre-commit
3
4echo " Running pre-commit checks..."
5
6# Run lint-staged
7echo " Checking code style..."
8npx lint-staged
9
10# Run affected tests only
11echo " Running tests for changed files..."
12npm run test:changed
13
14# Check if there are any TODO/FIXME comments in staged files
15echo " Checking for TODO/FIXME comments..."
16TODOS=$(git diff --cached --name-only | xargs grep -l "TODO|FIXME" || true)
17if [ ! -z "$TODOS" ]; then
18 echo " Found TODO/FIXME comments in staged files:"
19 echo "$TODOS"
20 echo "Please resolve them before committing."
21 exit 1
22fi
23
24# Check for debugging statements
25echo " Checking for debugging statements..."
26DEBUG_STATEMENTS=$(git diff --cached --name-only | xargs grep -l "console.log|debugger" || true)
27if [ ! -z "$DEBUG_STATEMENTS" ]; then
28 echo " Found debugging statements in staged files:"
29 echo "$DEBUG_STATEMENTS"
30 echo "Please remove them before committing."
31 exit 1
32fi
33
34echo "All pre-commit checks passed! Ready to march!"

Branch Protection Rules

GitHub Branch Protection

1# .github/branch-protection.yml
2branch_protection_rules:
3 main:
4 required_status_checks:
5 strict: true
6 contexts:
7 - "Code Quality"
8 - "Unit Tests"
9 - "Integration Tests"
10 - "E2E Tests"
11 - "Security Scan"
12 enforce_admins: true
13 required_pull_request_reviews:
14 required_approving_review_count: 2
15 dismiss_stale_reviews: true
16 require_code_owner_reviews: true
17 restrictions:
18 users: []
19 teams: ["senior-legionaries"]

Automated Testing Strategies

Test Organization

1// test/automation/test-suites.ts
2export class AutomatedTestSuites {
3 static getCriticalTests(): TestSuite[] {
4 return [
5 {
6 name: 'Authentication Flow',
7 tests: [
8 'user-login.e2e.spec.ts',
9 'user-registration.e2e.spec.ts',
10 'password-reset.e2e.spec.ts'
11 ],
12 timeout: 30000,
13 retries: 2
14 },
15 {
16 name: 'Core Business Logic',
17 tests: [
18 'tribute-management.spec.ts',
19 'expedition-planning.spec.ts',
20 'legion-assignment.spec.ts'
21 ],
22 timeout: 10000,
23 retries: 1
24 },
25 {
26 name: 'Data Integrity',
27 tests: [
28 'database-transactions.spec.ts',
29 'data-validation.spec.ts',
30 'backup-restore.spec.ts'
31 ],
32 timeout: 60000,
33 retries: 0
34 }
35 ];
36 }
37
38 static getPerformanceTests(): TestSuite[] {
39 return [
40 {
41 name: 'Load Testing',
42 tests: ['load-test.spec.ts'],
43 schedule: 'nightly',
44 environment: 'staging'
45 },
46 {
47 name: 'Stress Testing',
48 tests: ['stress-test.spec.ts'],
49 schedule: 'weekly',
50 environment: 'performance'
51 }
52 ];
53 }
54}

Smart Test Selection

1// test/automation/smart-test-runner.ts
2import { execSync } from 'child_process';
3
4export class SmartTestRunner {
5 static getAffectedTests(changedFiles: string[]): string[] {
6 const affectedTests = new Set<string>();
7
8 changedFiles.forEach(file => {
9 // Add tests for changed files
10 const testFile = this.getTestFileForSource(file);
11 if (testFile) {
12 affectedTests.add(testFile);
13 }
14
15 // Add integration tests if API was changed
16 if (this.isApiFile(file)) {
17 this.getIntegrationTests().forEach(test => affectedTests.add(test));
18 }
19
20 // Add E2E tests if controllers were changed
21 if (this.isControllerFile(file)) {
22 this.getE2ETests().forEach(test => affectedTests.add(test));
23 }
24 });
25
26 return Array.from(affectedTests);
27 }
28
29 static getChangedFiles(): string[] {
30 const gitDiff = execSync('git diff --name-only HEAD~1', { encoding: 'utf8' });
31 return gitDiff.trim().split('\n').filter(Boolean);
32 }
33
34 private static getTestFileForSource(sourceFile: string): string | null {
35 const testFile = sourceFile.replace(/\.ts$/, '.spec.ts');
36 return this.fileExists(testFile) ? testFile : null;
37 }
38
39 private static isApiFile(file: string): boolean {
40 return file.includes('controller') || file.includes('dto') || file.includes('interface');
41 }
42
43 private static isControllerFile(file: string): boolean {
44 return file.includes('controller.ts');
45 }
46}

Automated Reporting

Test Results Dashboard

1// test/automation/test-reporter.ts
2export class TestReporter {
3 static generateReport(testResults: TestResult[]): TestReport {
4 const totalTests = testResults.length;
5 const passedTests = testResults.filter(t => t.status === 'passed').length;
6 const failedTests = testResults.filter(t => t.status === 'failed').length;
7 const skippedTests = testResults.filter(t => t.status === 'skipped').length;
8
9 const coverage = this.calculateCoverage(testResults);
10 const performance = this.analyzePerformance(testResults);
11
12 return {
13 summary: {
14 total: totalTests,
15 passed: passedTests,
16 failed: failedTests,
17 skipped: skippedTests,
18 successRate: (passedTests / totalTests) * 100
19 },
20 coverage,
21 performance,
22 failedTests: testResults.filter(t => t.status === 'failed'),
23 slowTests: testResults
24 .filter(t => t.duration > 5000)
25 .sort((a, b) => b.duration - a.duration),
26 recommendations: this.generateRecommendations(testResults)
27 };
28 }
29
30 static async sendSlackNotification(report: TestReport): Promise<void> {
31 const color = report.summary.successRate >= 95 ? 'good' :
32 report.summary.successRate >= 80 ? 'warning' : 'danger';
33
34 const message = {
35 text: 'Legionary Cohort Test Results',
36 attachments: [{
37 color,
38 fields: [
39 {
40 title: 'Success Rate',
41 value: `${report.summary.successRate.toFixed(1)}%`,
42 short: true
43 },
44 {
45 title: 'Tests',
46 value: `${report.summary.passed}/${report.summary.total}`,
47 short: true
48 },
49 {
50 title: 'Coverage',
51 value: `${report.coverage.statements}%`,
52 short: true
53 },
54 {
55 title: 'Failed Tests',
56 value: report.failedTests.length.toString(),
57 short: true
58 }
59 ]
60 }]
61 };
62
63 await this.sendToSlack(message);
64 }
65}

Automated Rollback

1# .github/workflows/auto-rollback.yml
2name: Auto Rollback on Failed Tests
3
4on:
5 deployment_status:
6
7jobs:
8 health-check:
9 if: github.event.deployment_status.state == 'success'
10 runs-on: ubuntu-latest
11
12 steps:
13 - name: Wait for deployment to stabilize
14 run: sleep 60
15
16 - name: Run health checks
17 id: health-check
18 run: |
19 # Critical endpoints health check
20 curl -f ${{ github.event.deployment.payload.web_url }}/health
21 curl -f ${{ github.event.deployment.payload.web_url }}/api/tributes
22 curl -f ${{ github.event.deployment.payload.web_url }}/api/expeditions
23
24 - name: Run smoke tests
25 if: steps.health-check.outcome == 'success'
26 run: npm run test:smoke
27
28 - name: Trigger rollback if tests fail
29 if: failure()
30 uses: actions/github-script@v6
31 with:
32 script: |
33 github.rest.repos.createDeployment({
34 owner: context.repo.owner,
35 repo: context.repo.repo,
36 ref: '${{ github.event.deployment.payload.previous_version }}',
37 environment: '${{ github.event.deployment.environment }}',
38 description: 'Auto rollback due to failed health checks'
39 });

Monitoring and Alerting

Test Performance Monitoring

1// test/automation/performance-monitor.ts
2export class TestPerformanceMonitor {
3 private static readonly PERFORMANCE_THRESHOLDS = {
4 unit: 50, // ms per test
5 integration: 500, // ms per test
6 e2e: 5000, // ms per test
7 total: 300000 // 5 minutes total
8 };
9
10 static analyzeTestPerformance(results: TestResult[]): PerformanceAlert[] {
11 const alerts: PerformanceAlert[] = [];
12
13 // Check individual test performance
14 results.forEach(test => {
15 const threshold = this.PERFORMANCE_THRESHOLDS[test.type] || this.PERFORMANCE_THRESHOLDS.unit;
16
17 if (test.duration > threshold * 2) {
18 alerts.push({
19 type: 'CRITICAL',
20 test: test.name,
21 message: `Test is ${Math.round(test.duration / threshold)}x slower than threshold`,
22 duration: test.duration,
23 threshold
24 });
25 } else if (test.duration > threshold * 1.5) {
26 alerts.push({
27 type: 'WARNING',
28 test: test.name,
29 message: `Test is approaching performance threshold`,
30 duration: test.duration,
31 threshold
32 });
33 }
34 });
35
36 // Check total test suite duration
37 const totalDuration = results.reduce((sum, test) => sum + test.duration, 0);
38 if (totalDuration > this.PERFORMANCE_THRESHOLDS.total) {
39 alerts.push({
40 type: 'CRITICAL',
41 test: 'Test Suite',
42 message: `Total test duration exceeds maximum allowed time`,
43 duration: totalDuration,
44 threshold: this.PERFORMANCE_THRESHOLDS.total
45 });
46 }
47
48 return alerts;
49 }
50}

Test Automation is a powerful autopilot system for your Roman fort! Thanks to it, you can sleep peacefully knowing that every code change is automatically checked by the best legion of testers in the Empire!

Go to CodeWorlds