Yes, I am clearly saying “don’t UNIT test I/O”, but not just in JS. In any language.
I/O is communication: a collaboration by definition, and a proper test of communication doesn’t simply ask “did we say the right thing? It also asks “was the right thing received?” and “was the response correct?” and usually, “did we handle the response correctly?”
You say this is often “impossible” in the real world, but I’ve been writing integration tests for more than a decade and I have NEVER encountered a situation in all that time when I could not write a proper integration test for at least the complete flow of one side.
Sometimes we’re writing an integration test for an expensive 3rd party API that you can’t run on your own servers, but even in that case, you can record real key interractions and run mock instances on your own test servers that behave like the real thing for the purpose of your integration tests.
Simply point your URL configurations to the mock instances in your testing environment. I inject all top level URLs into app config using environment variables (Google 12-factor app).
Even then you don’t need to manually write mocks. There are tools that will automatically record and replay server responses. To record server interractions, you can use nock, Chrome dev tools, or curl.
Serving up recorded responses is borderline trivial, even using Node & Express.
The questions you need to answer in integration tests are not just “did I call the right function with the right parameters?” but also, “was the message bundled with the correct headers and response codes?”
In other words, “is the message actually getting communicated properly?”
You can’t do that by simply mocking the http call function at one end.
If you want to ask the question, “did I call the right thing with the right parameters? My favorite way is not to mock: it’s to abstract away from the actual side-effects using monads or generator functions, or a combination. See the redux-saga API for lots more detail on that. And — yes you can use Redux on the server. You just do it in the request/response cycle on a per-request basis. Hurray!