Strategic architecture, tactical design, and testable code principles (SOLID, Clean Architecture, Design Patterns, Testable Design)
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Clean architecture makes testing easy by design. If something is hard to test, it's a sign of architectural coupling. Don't compromise architecture for testability; fix the coupling.
Incorrect (hard to test, requiring workarounds):
public class OrderProcessor {
public void process(Order order) {
// Hard to test: direct instantiation
PaymentService payment = new PaymentService();
// Hard to test: static call
if (InventoryChecker.isAvailable(order.getItems())) {
// Hard to test: singleton
payment.charge(order.getTotal());
NotificationService.getInstance().sendConfirmation(order);
// Hard to test: current time
order.setProcessedAt(new Date());
}
}
}
// Test requires PowerMock, static mocking, singleton reset
// Test is slow, brittle, and complicated
@Test
@PrepareForTest({InventoryChecker.class, NotificationService.class})
public void testProcess() {
PowerMockito.mockStatic(InventoryChecker.class);
PowerMockito.when(InventoryChecker.isAvailable(any())).thenReturn(true);
// ... 20 more lines of mock setup
}Correct (testable by design):
public class OrderProcessor {
private final InventoryChecker inventory;
private final PaymentGateway payment;
private final NotificationPort notification;
private final Clock clock;
// All dependencies injected - easy to substitute
public OrderProcessor(
InventoryChecker inventory,
PaymentGateway payment,
NotificationPort notification,
Clock clock
) {
this.inventory = inventory;
this.payment = payment;
this.notification = notification;
this.clock = clock;
}
public ProcessResult process(Order order) {
if (!inventory.isAvailable(order.getItems())) {
return ProcessResult.unavailable();
}
PaymentResult paymentResult = payment.charge(order.getTotal());
if (!paymentResult.isSuccessful()) {
return ProcessResult.paymentFailed(paymentResult.getError());
}
order.markProcessed(clock.now());
notification.sendConfirmation(order);
return ProcessResult.success(order);
}
}
@Test
void processesOrderWhenInventoryAvailable() {
var inventory = StubInventory.withAvailability(true);
var payment = FakePaymentGateway.alwaysSucceeds();
var notification = new SpyNotification();
var clock = Clock.fixed(Instant.parse("2024-01-15T10:00:00Z"), ZoneOffset.UTC);
var processor = new OrderProcessor(inventory, payment, notification, clock);
var result = processor.process(order);
assertThat(result.isSuccessful()).isTrue();
}Testability checklist:
new for services (inject dependencies)Reference: Growing Object-Oriented Software, Guided by Tests
clean-architecture
evals
references
design-patterns
solid-principles
testable-design