> "But you must test against the API, not the implementation details!"
I think this is a partial truth. Testing against behavioural boundaries is my preferred method. Whether that boundary is an API, a public method, or the entry to an internal behaviour. TDD can influence this approach to ensure that TDD means Test Driven Design, if you're testing at behavioural boundaries then implementation details can still be changed.
The question then becomes: can you identify the behavioural boundaries of your code, and if not why not?
(generic "you", not directed at devlead)
Hot take: There's nothing inherently wrong with testing implementation details
I'm sorry if you spit out your coffee when you read that, but hear me out before you smash a novel out on your mechanical keyboard to fight me on this.
My approach to testing software boils down to one fundamental:
Building confidence in the changes we're delivering
So if you're preparing your debate points, just know that this is the perspective I'm grounded in.
How we build confidence can and will look different from situation to situation.
When I build parsers, I almost always go straight to TDD. I don't even love TDD, but I treat my system like a blackbox and test inputs and outputs. I am constantly rearranging the guts of my parsing logic and I just want to make sure I get the right stuff out based on what I put in.
When I build bigger or more complex systems, this story changes. Quite a bit. Just because there are different scenarios I need to consider.
"But you must test against the API, not the implementation details!"
When I'm writing code, there are some things that aren't known on the API but they're behaviors I expect. And if I need confidence on those things, you can bet your butt that I'm going to write tests to give me that confidence.
Some things that I can't just "test against the API" of the method I'm calling:
- Does my call to an external service handle exceptions properly?
- If necessary, does that call handle retries accordingly?
- Before we go roll out a deployment worldwide to hundreds of thousands of servers, are we sending the right telemetry?
- Do we have the right logging in place for helpful debug information?
... There are countless scenarios I've encountered where it's been helpful to test implementation details.
"But then your tests become brittle! You can never change the code now!!!"
Look, you're changing the code without the coverage and breaking all sorts of expected behavior because it wasn't "against the API" so there were no tests.
Now if you made it this far before you rushed to the comments to rage:
- No, I don't think this is the only way to test
- Yes, I would (generally) prefer system/integration tests since they've become very cheap/fast to run
- No, I don't think any one type of test is superior to all of them
- Yes, I do mix all sorts of testing into my codebase
- Yes, I refactor code non-stop
- Yes, there are absolutely times I have to blow away tests when I rewrite the guts of something
But along the way I'm deciding where I need to build confidence with all of the tools at my disposal.