Writing functions that return side effects as a message instead of running them within the function must be one of the most underrated things.
function foo() : void {
doBar();
if (...) {
doBaz(12);
}
}
to something like: function fooEffects(): Effects[] {
results = [ { type: 'bar' }]
if (....) {
results.push({ type: 'baz', value: 12 })
}
return result
}
function executeEffects(effects: Effects) {
if (effects.type === 'bar') {
....
}
if (effects.type === 'baz') {
...
}
}
Then you can easily test the "fooEffects" function (it just return values that you can compare to the expected effects").The `executeEffects` is still hard to test (you must mock / fake / stub things), but at least you only have to setup the mocking once.
Obviously, all hells break loose if you need to test some effects that depend on the result of other effects, which limits the technique to "single flow" of effects (printing stuff, sending commands, etc...)