I’ve looked over some of my code lately, and found ways that I often improve my tests. I’m planning on writing a blog post for each of my five favorites.
First out: Using the data storage. I upgraded our API for billing customers. I had a few compilation errors in my code, as the API had changed somewhat. After fixing these errors, I was left with a test that broke mysteriously with a MethodNotFoundException
. I located the test and found that it was close to unreadable. After examining it further, I found that it was trying to do was simple, but hidden in technical code.
I had the following test (the design is real, the details have been changed to protect the guilty):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@Test public void shouldBillExtraForErrors() { BillingService billingService = someService.getBillingService(); MethodCallLoggingInterceptor interceptor = MethodCallLoggingInterceptor.for(billingService); someService.setBillingService((BillingService)interceptor); Request requestWithError = createCustomerRequestWithError(CUSTOMER_ID); someService.process(requestWithError); Method method = BillingService.class.getMethod("sendBillingEvent", ....); MethodCall[] callsToBilling = interceptor.getMethodCallsFor(method); assertEquals(2, callsToBilling); assertEquals(CUSTOMER_ID, callsToBilling[0].getParams()[0]); assertEquals(BillingCode.RECEIVED_REQUEST, callsToBilling[0].getParams()[3]); assertEquals(CUSTOMER_ID, callsToBilling[1].getParams()[0]); assertEquals(BillingCode.INVALID_REQUEST, callsToBilling[1].getParams()[3]); } |
When the parameters to sendBillingEvent
changed and the test started throwing MethodNotFoundException
I changed it to the following:
1 2 3 4 5 6 7 8 9 10 11 |
@Test public void shouldBillExtraForErrors() { Request requestWithError = createCustomerRequestWithError(CUSTOMER_ID); someService.process(requestWithError); BillingFinder finder = BillingFinder.forCustomer(CUSTOMER_ID); finder.setBillingCode(BillingCode.RECEIVED_REQUEST); assertEquals(1, billingRepository.count(finder)); finder.setBillingCode(BillingCode. INVALID_REQUEST); assertEquals(1, billingRepository.count(finder)); } |
The lessons:
- Ask about the outcome, not about the method calls that get you there
- Avoid reflection in tests. It’s bound to be brittle
- Mocks and similar methods are overrated
(In tip #2, I will show how I preserve the data store metaphor without giving up execution speed)