Testing Image Processing Pipelines with Sample Files
Image processing pipelines need diverse test inputs — different formats, sizes, transparency, and edge cases. This guide shows how to build a thorough test suite using sample images from TrueFileSize.
Prerequisites
- Node.js 18+ with sharp, or Python 3.10+ with Pillow
- Sample images from TrueFileSize
Step 1: Download Diverse Sample Images
mkdir -p test-images
CDN="https://cdn.truefilesize.com"
# Standard formats and sizes
curl -sL -o test-images/photo-1mb.jpg "$CDN/jpg/sample-1mb.jpg"
curl -sL -o test-images/photo-5mb.jpg "$CDN/jpg/sample-5mb.jpg"
curl -sL -o test-images/screenshot.png "$CDN/png/sample-500kb.png"
# Transparency (alpha channel)
curl -sL -o test-images/logo-transparent.png "$CDN/png/transparent-logo.png"
# Modern formats
curl -sL -o test-images/modern.webp "$CDN/webp/sample-100kb.webp"
curl -sL -o test-images/next-gen.avif "$CDN/avif/sample-100kb.avif"
# Edge cases
curl -sL -o test-images/tiny-icon.png "$CDN/png/sample-icon-32.png"
curl -sL -o test-images/large-4k.png "$CDN/png/sample-5mb.png"
curl -sL -o test-images/grayscale.png "$CDN/png/sample-grayscale.png"
Available: JPG · PNG · WebP · AVIF · SVG · GIF
Step 2: Test Resize (Node.js / sharp)
import sharp from 'sharp';
import { readFileSync, statSync } from 'fs';
describe('Image Resize', () => {
it('resizes JPG to 800x600', async () => {
const output = await sharp('test-images/photo-1mb.jpg')
.resize(800, 600, { fit: 'cover' })
.jpeg({ quality: 85 })
.toBuffer();
const meta = await sharp(output).metadata();
expect(meta.width).toBe(800);
expect(meta.height).toBe(600);
expect(output.length).toBeLessThan(statSync('test-images/photo-1mb.jpg').size);
});
it('creates thumbnail from 4K image', async () => {
const output = await sharp('test-images/large-4k.png')
.resize(200, 200, { fit: 'inside' })
.png()
.toBuffer();
const meta = await sharp(output).metadata();
expect(meta.width).toBeLessThanOrEqual(200);
expect(meta.height).toBeLessThanOrEqual(200);
});
});
Step 3: Test Format Conversion
describe('Format Conversion', () => {
it('converts JPG to WebP', async () => {
const output = await sharp('test-images/photo-1mb.jpg')
.webp({ quality: 80 })
.toBuffer();
const meta = await sharp(output).metadata();
expect(meta.format).toBe('webp');
// WebP should be smaller than JPG
expect(output.length).toBeLessThan(
statSync('test-images/photo-1mb.jpg').size
);
});
it('converts JPG to AVIF', async () => {
const output = await sharp('test-images/photo-1mb.jpg')
.avif({ quality: 50 })
.toBuffer();
const meta = await sharp(output).metadata();
expect(meta.format).toBe('heif'); // sharp reports AVIF as heif
});
it('converts PNG to WebP preserving transparency', async () => {
const output = await sharp('test-images/logo-transparent.png')
.webp({ quality: 90 })
.toBuffer();
const meta = await sharp(output).metadata();
expect(meta.format).toBe('webp');
expect(meta.hasAlpha).toBe(true); // Transparency preserved
});
});
Step 4: Test Transparency Preservation
describe('Transparency', () => {
it('preserves alpha channel in PNG → WebP', async () => {
const output = await sharp('test-images/logo-transparent.png')
.webp()
.toBuffer();
const { channels, hasAlpha } = await sharp(output).metadata();
expect(hasAlpha).toBe(true);
expect(channels).toBe(4); // RGBA
});
it('flattens transparency to white for JPG', async () => {
const output = await sharp('test-images/logo-transparent.png')
.flatten({ background: { r: 255, g: 255, b: 255 } })
.jpeg()
.toBuffer();
const { channels, hasAlpha } = await sharp(output).metadata();
expect(hasAlpha).toBe(false);
expect(channels).toBe(3); // RGB only
});
});
Step 5: Test Optimization Pipeline
describe('Optimization', () => {
it('reduces file size by 50%+', async () => {
const input = readFileSync('test-images/photo-5mb.jpg');
const output = await sharp(input)
.resize(1920, 1080, { fit: 'inside', withoutEnlargement: true })
.jpeg({ quality: 80, mozjpeg: true })
.toBuffer();
const reduction = 1 - output.length / input.length;
expect(reduction).toBeGreaterThan(0.5); // >50% reduction
});
it('strips EXIF metadata', async () => {
const output = await sharp('test-images/photo-1mb.jpg')
.rotate() // Auto-rotate based on EXIF
.jpeg()
.toBuffer();
const meta = await sharp(output).metadata();
expect(meta.orientation).toBeUndefined();
});
});
Python (Pillow)
from PIL import Image
from pathlib import Path
def test_resize():
img = Image.open('test-images/photo-1mb.jpg')
resized = img.resize((800, 600), Image.LANCZOS)
assert resized.size == (800, 600)
def test_transparency_preserved():
img = Image.open('test-images/logo-transparent.png')
assert img.mode == 'RGBA'
webp_path = '/tmp/output.webp'
img.save(webp_path, 'WEBP', quality=90)
result = Image.open(webp_path)
assert result.mode == 'RGBA'
def test_format_conversion():
img = Image.open('test-images/photo-1mb.jpg')
img.save('/tmp/output.webp', 'WEBP', quality=80)
assert Path('/tmp/output.webp').stat().st_size < Path('test-images/photo-1mb.jpg').stat().st_size
Test Matrix
| Test | Input File | Operation | Verify | |------|-----------|-----------|--------| | Resize | 1MB JPG | 800×600 cover | Dimensions, smaller size | | Thumbnail | 5MB PNG | 200×200 inside | Max dimension ≤200 | | JPG→WebP | 1MB JPG | webp q80 | Format, smaller size | | JPG→AVIF | 1MB JPG | avif q50 | Format check | | PNG→WebP (alpha) | transparent PNG | webp q90 | hasAlpha=true | | PNG→JPG (flatten) | transparent PNG | flatten+jpeg | hasAlpha=false | | Optimize | 5MB JPG | resize+mozjpeg | >50% reduction | | Grayscale | grayscale PNG | Convert | channels=1 | | 32×32 icon | icon PNG | Resize up | Quality check |