Performance Testing
Performance testing ensures applications meet performance requirements. This article covers testing strategies and tools.
Introduction
Performance testing provides:
- Load testing
- Stress testing
- Profiling
- Bottleneck identification
- Optimization guidance
Understanding performance testing helps you:
- Identify bottlenecks
- Optimize applications
- Ensure scalability
- Improve user experience
- Plan capacity
Load Testing
Apache JMeter
# โ
Good: Install JMeter
# Download from https://jmeter.apache.org/
# โ
Good: Run load test
jmeter -n -t test-plan.jmx -l results.jtl -j jmeter.log
# โ
Good: Generate report
jmeter -g results.jtl -o report/
k6 Load Testing
// โ
Good: Install k6
// npm install -g k6
// โ
Good: k6 test script
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '30s', target: 20 }, // Ramp up
{ duration: '1m30s', target: 100 }, // Stay at 100
{ duration: '30s', target: 0 } // Ramp down
]
};
export default function () {
const response = http.get('http://localhost:3000/api/users');
check(response, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500
});
sleep(1);
}
// โ
Good: Run k6 test
// k6 run load-test.js
// โ
Good: k6 with thresholds
export const options = {
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.1']
}
};
Artillery Load Testing
# โ
Good: Artillery config
config:
target: 'http://localhost:3000'
phases:
- duration: 60
arrivalRate: 10
- duration: 120
arrivalRate: 50
- duration: 60
arrivalRate: 10
scenarios:
- name: 'Get Users'
flow:
- get:
url: '/api/users'
- think: 5
- get:
url: '/api/users/{{ userId }}'
- name: 'Create User'
flow:
- post:
url: '/api/users'
json:
name: 'John'
email: '[email protected]'
Stress Testing
Stress Test Script
// โ
Good: Stress test with k6
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '2m', target: 200 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 300 },
{ duration: '5m', target: 300 },
{ duration: '2m', target: 0 }
],
thresholds: {
http_req_duration: ['p(99)<1000'],
http_req_failed: ['rate<0.05']
}
};
export default function () {
const response = http.get('http://localhost:3000/api/users');
check(response, {
'status is 200': (r) => r.status === 200
});
}
Profiling
Node.js Profiling
// โ
Good: CPU profiling
const v8Profiler = require('v8-profiler-next');
v8Profiler.startProfiling('test');
// Run code to profile
for (let i = 0; i < 1000000; i++) {
Math.sqrt(i);
}
const profile = v8Profiler.stopProfiling('test');
profile.export((err, result) => {
require('fs').writeFileSync('profile.cpuprofile', result);
profile.delete();
});
// โ
Good: Memory profiling
const heapdump = require('heapdump');
// Trigger heap dump
heapdump.writeSnapshot(`./heap-${Date.now()}.heapsnapshot`);
// โ
Good: Flame graphs
// npm install -g 0x
// 0x node app.js
// Open http://localhost:7002
Chrome DevTools Profiling
// โ
Good: Profile in browser
// 1. Open DevTools (F12)
// 2. Go to Performance tab
// 3. Click Record
// 4. Perform actions
// 5. Click Stop
// 6. Analyze results
// โ
Good: Lighthouse performance audit
// 1. Open DevTools
// 2. Go to Lighthouse tab
// 3. Click Generate report
// 4. Review recommendations
Benchmarking
Benchmark.js
// โ
Good: Install Benchmark.js
// npm install benchmark
const Benchmark = require('benchmark');
const suite = new Benchmark.Suite;
// Add tests
suite
.add('Array#indexOf', () => {
[1, 2, 3, 4, 5].indexOf(3);
})
.add('Array#findIndex', () => {
[1, 2, 3, 4, 5].findIndex(x => x === 3);
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({ 'async': true });
Custom Benchmarking
// โ
Good: Simple benchmark
function benchmark(fn, iterations = 1000000) {
const start = process.hrtime.bigint();
for (let i = 0; i < iterations; i++) {
fn();
}
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1000000; // Convert to ms
console.log(`${fn.name}: ${duration.toFixed(2)}ms`);
}
// โ
Good: Compare implementations
benchmark(() => {
const arr = [1, 2, 3, 4, 5];
arr.indexOf(3);
}, 1000000);
benchmark(() => {
const arr = [1, 2, 3, 4, 5];
arr.find(x => x === 3);
}, 1000000);
Performance Optimization
Frontend Optimization
// โ
Good: Measure performance
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`${entry.name}: ${entry.duration}ms`);
}
});
observer.observe({ entryTypes: ['measure', 'navigation'] });
// โ
Good: Mark and measure
performance.mark('start');
// Do work
performance.mark('end');
performance.measure('work', 'start', 'end');
// โ
Good: Web Vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);
Backend Optimization
// โ
Good: Query optimization
// Bad: N+1 queries
const users = await User.find();
for (const user of users) {
user.posts = await Post.find({ userId: user._id });
}
// Good: Use populate
const users = await User.find().populate('posts');
// โ
Good: Caching
const cache = new Map();
async function getUser(id) {
if (cache.has(id)) {
return cache.get(id);
}
const user = await User.findById(id);
cache.set(id, user);
return user;
}
// โ
Good: Connection pooling
const pool = new Pool({
max: 20,
idleTimeoutMillis: 30000
});
Performance Monitoring
Real User Monitoring
// โ
Good: Send performance data
function sendPerformanceData() {
const perfData = performance.getEntriesByType('navigation')[0];
const data = {
dns: perfData.domainLookupEnd - perfData.domainLookupStart,
tcp: perfData.connectEnd - perfData.connectStart,
ttfb: perfData.responseStart - perfData.requestStart,
download: perfData.responseEnd - perfData.responseStart,
domInteractive: perfData.domInteractive - perfData.fetchStart,
domComplete: perfData.domComplete - perfData.fetchStart,
loadComplete: perfData.loadEventEnd - perfData.fetchStart
};
fetch('/api/performance', {
method: 'POST',
body: JSON.stringify(data)
});
}
window.addEventListener('load', sendPerformanceData);
Best Practices
-
Test realistic scenarios:
// โ Good: Realistic load test // Simulate real user behavior // Use realistic data // Test peak hours // โ Bad: Unrealistic test // Simple requests // Minimal data // No variation -
Monitor key metrics:
// โ Good: Monitor key metrics // - Response time (p50, p95, p99) // - Throughput (requests/sec) // - Error rate // - Resource usage // โ Bad: No monitoring -
Establish baselines:
// โ Good: Establish baselines // Measure current performance // Set targets // Track improvements // โ Bad: No baselines
Summary
Performance testing is essential. Key takeaways:
- Conduct load testing
- Perform stress testing
- Profile applications
- Benchmark code
- Monitor performance
- Optimize bottlenecks
- Establish baselines
- Test continuously
Related Resources
Next Steps
- Learn about Testing & QA
- Explore Integration Testing
- Study E2E Testing
- Practice performance testing
- Optimize applications
Comments