Every line of test setup is a smell. If you can't tell what's relevant to your assertion, neither can anyone else. Irrelevant setup is noise. Noise hides bugs.
email="banned@example.com", not email="test@test.com".Tests accumulate setup over time. Someone adds a field. Someone else adds another. No one removes anything "just in case."
Soon you have 50 lines of setup and 1 line of assertion:
def test_discount_applies_for_premium_users():
user = create_user(
name="Alice Johnson",
email="alice.johnson@example.com",
phone="555-0100",
address="123 Main St",
city="Springfield",
state="IL",
zip="62701",
country="USA",
age=34,
gender="F",
preferences={"newsletter": True, "sms": False},
created_at=datetime(2023, 1, 15),
last_login=datetime(2024, 12, 1),
membership_tier="premium", # ← Only this matters
payment_methods=[
{"type": "credit_card", "last4": "1234", "exp": "12/25"},
{"type": "paypal", "email": "alice@paypal.com"}
],
billing_history=[...], # 20 more lines
)
cart = create_cart(user)
add_item(cart, "widget", price=100.00, quantity=1)
discount = apply_discount(cart) # ← The actual test
assert discount == 10.00 # 10% for premium usersWhat's relevant? You can't tell. Is the email relevant? The billing history? The age?
When this test fails, you'll waste 10 minutes reading irrelevant setup trying to understand what broke.
// 😱 BAD: Everything and the kitchen sink
test('validates email format', () => {
const user = createUser({
firstName: 'John',
lastName: 'Doe',
email: 'invalid-email', // ← Only this matters
password: 'SecureP@ssw0rd!',
age: 28,
phone: '555-0123',
address: {
street: '456 Oak Ave',
city: 'Portland',
state: 'OR',
zip: '97201',
},
preferences: {
theme: 'dark',
notifications: true,
language: 'en-US',
},
roles: ['user', 'contributor'],
metadata: {
source: 'web',
campaign: 'spring2024',
},
});
const result = validateUser(user);
expect(result.valid).toBe(false);
expect(result.errors).toContain('Invalid email format');
});
// 30 lines of setup. 1 field matters. Which one? Who knows.// ✅ GOOD: Only what's necessary
test('validates email format', () => {
const user = {
email: 'invalid-email', // ← Crystal clear what matters
};
const result = validateEmail(user.email);
expect(result.valid).toBe(false);
expect(result.error).toBe('Invalid email format');
});
// 1 line of setup. Zero confusion. Obvious what's being tested.# 😱 BAD: Critical data hidden in factories
def test_cannot_delete_active_subscription():
user = create_premium_user() # What does this create?
result = user.cancel_subscription()
assert result.error == "Cannot cancel active subscription"
# What's a "premium_user"?
# - Do they have billing info?
# - Are they on a trial?
# - Do they have payment history?
# You have to read create_premium_user() to find out.
def create_premium_user():
# Hidden in a factory, 100 lines away
return User(
membership="premium",
trial_ends=None, # ← This is why cancellation fails!
billing_date=datetime.now(),
payment_method="credit_card",
# ... 50 more fields
)# ✅ GOOD: Critical data in the test
def test_cannot_cancel_active_subscription():
user = User(
membership="premium",
trial_ends=None, # ← Right here, obvious
)
result = user.cancel_subscription()
assert result.error == "Cannot cancel active subscription"
# Now it's obvious: trial_ends=None means active subscription.
# No hunting through factory functions.
# If you need defaults for other tests, make them explicit:
def user_with_defaults(**overrides):
return User(
membership="free",
trial_ends=None,
**overrides # Caller overrides what matters
)// 😱 BAD: Generic, meaningless data
func TestBlocksSuspendedUsers(t *testing.T) {
user := User{
ID: 1, // What's special about user 1?
Email: "test@test.com", // Why this email?
Name: "Test User",
Status: "suspended", // ← This is what matters, but it's buried
}
result := authenticateUser(user.Email, "password")
assert.False(t, result.Allowed)
}
// The test passes, but you don't know WHY from reading it.// ✅ GOOD: Data tells the story
func TestBlocksSuspendedUsers(t *testing.T) {
user := User{
Email: "suspended@example.com", // ← Name tells you what's special
Status: "suspended", // ← Front and center
}
result := authenticateUser(user.Email, "password")
assert.False(t, result.Allowed)
assert.Equal(t, "Account suspended", result.Reason)
}
// Now you know EXACTLY why the test should fail auth.
// The email itself documents the test case.For every line of test setup, ask: "If I delete this, does the test still make sense?"
If the answer is yes, delete it.
Irrelevant setup isn't just clutter. It's a lie. It suggests something matters when it doesn't. That misleads readers and hides bugs.
Factories are fine for required but irrelevant fields:
// ✅ Factory handles required-but-boring fields
function createOrder(overrides = {}) {
return {
id: randomUUID(), // Required by DB, irrelevant to test
createdAt: new Date(), // Required, usually irrelevant
status: 'pending', // Good default
...overrides, // Caller specifies what matters
};
}
test('ships order when payment clears', () => {
const order = createOrder({
status: 'paid', // ← Inline what matters
items: [{ sku: 'WIDGET', qty: 1 }],
});
shipOrder(order);
expect(order.status).toBe('shipped');
});The factory handles boilerplate (ID, timestamp). The test specifies what's being tested (status, items).
If you can't tell what's being tested by reading the setup, fix the setup.
Every line of setup should answer the question: "Why is this here?"
If the answer is "I don't know" or "just in case," delete it.
"The best test setup is the setup you don't have."