In my other post about
creating properly typechecked helper functions in Typescript, I missed something out from the
examples that's present in the real code: default parameters! There's a function,
that returns an object with a dynamically generated UUID value in it. It also includes the current
timestamp. This is great from the programmer's point of view as there's no need to wire up these
values manually, but it sucks for unit tests because the values always change! The solution is to
to keep the ergonomics of a clean public API, but still support unit testing and special cases
Let's say we have the following function:
Pretty standard. Calling it is ergonomic:
Great, my ground-breaking code goes live yet again. Or would if I had any unit tests! Fine. Bloody review process.
A pretty contrived test, but good enough for the purposes of this post. Naturally, it fails.
Because UUIDs are unique, the value returned by
createUser will be different every time, failing
expect() equality check. Similarly, the timestamp returned is always the current timestamp
so that's of no use to compare against either.
One solution to this problem is to only check that some fields are returned, perhaps just
When the function is called normally as
_timestamp are left
undefined, so use their default values as used. These are
uuid) and a function that
returns the current timestamp. This doesn't change the normal interface to this function, however
now we can do much nicer stuff with our test:
The above changes hardcode the
created_at values, allowing the test to do a deep
comparison on the whole returned object. This simplifies the test code, and improves coverage by
ensuring that every field returned is what we expect. Adding tests like this to a well-typed
Typescript codebase should lead to more robust code and fewer (hopefully no) errors in production.
Fingers crossed anyway.
Now someone can approve my pull request. Hooray!
There's a somewhat major caveat to this method, and that's one of argument counts. What if I want to
add a third field
password to my handy
createUser function? That's easy enough, but now all the
existing code that uses
createUser will error out. The
_uuid argument is now being given a
string as input, which is of course not what we want it to do. Typescript will likely warn you in
this case, but maybe not. A solution I can think of is to pass all your arguments to the function as
an object with overridable functions defined after:
password is as easy as extending the first arguments object, and the "hidden" test
_timestamp are left neatly alone. Personally I think this is safer
and easier to reason about than positional string arguments, so take this as just another reason to
pass objects as function arguments.