|
|
|
|
|
by no_wizard
2491 days ago
|
|
I'm curious, and I'm prefacing this up front because I'm not always good at writing what I say in a way that may not feel like I'm coming from a good place, so here goes: Whats your testing story? This to me seems like not writing good, solid, abstracted tests before doing proper implementations of your code. This could be solved with good interface design, and perhaps be faster. I apologize in advance if this sounds harsh. This sounds like the exact thing folks on my team were trying to do, and it was turning things very sub-optimal. (Disclaimer: I'm a bit of a TDD/BDD idealogue. Not as hardcore as Uncle Bob[0], certainly, but close enough. I think writing Interfaces before Tests is acceptable, I think that might be where things differ, i guess). [0]https://blog.cleancoder.com/ |
|
The REST API is defined using OpenAPI v3, and we use express-openAPI to generate request and response validator functions for every endpoint. Each endpoint needs a happy-path test for 1) every optional argument supplied and 2) none of the optional arguments supplied (if there's no optional arguments then this devolves into a single test), and all side-effects must be verified. The main place where we use 'any' or {[key: string]: any} and then just cast to what's expected tends to be the responses from the database, because the response validation code will catch any actual mismatches (the most common mismatch is forgetting to parseInt and trying to send back something like '1' instead of 1, but sometimes there's issues with the db field being nullable when it shouldn't or not nullable when it should be nullable).
Here's the latest test run on master (hope the formatting works):
Branches are especially low, because we don't test most unhappy paths since we use a middleware error handler with generic error messages for different types of errors that's got something like >90% coverage instead of handling errors within each endpoint. The unhappy paths we do test thoroughly are things like our user-defined typeguards, our middleware error handler, our security handlers, and anything else where we validate unsafe/unknown inputs.We try to stay away from stringent TDD/BDD unit testing, because 1) the reward from the work required to get there doesn't justify the cost of getting there for us right now and 2) strict BDD/TDD unit testing makes it much harder and slower to try different implementations/refactor things, since every time you want to modify one-off helper functions you need to add a bunch of tests first. We found that cost is worth it at module boundaries (eg endpoints, auth, database), but not for most functions that are only ever called inside their module.