Chapter 9 of RxJS in action is available for free here and covers the topic in some detail if you want a deeper reading (full disclosure: I am one of the authors).
? TL; DR; Functional programming facilitates the transparent passing of arguments. Therefore, while you have taken a good step towards making your application more complex, you can go even further by making sure that your side effects are crowding out your application.
What does it look like in practice? Well, one funny template in Javascript is the currying function, which allows you to create functions that map to other functions. Thus, for your example, we could convert the amqlib injection to an argument instead:
import { createConnection, createChannel } from './'; export default (amqlib) => ({ host }) => createConnection(amqlib, host) .mergeMap(createChannel);
Now you will use it like this:
import builder from './amqlib-adapter' import amqlib from 'amqlib'
You can take another step, as well as introduce other createConnection and createChannel . Although, if you could make them pure functions, then by definition, everything consisting of them would also be a pure function.
What does it mean?
If I give you two functions:
const add => (a, b) => a + b; const mul => (a, b) => a * b;
Or generalized as curried functions:
const curriedAdd => (a) => (b) => a + b; const curriedMul => (a) => (b) => a * b;
Both add and multi are considered pure functions, that is, with the same set of inputs the result will be the same (read: there are no side effects). You will also hear that this is called referential transparency (it costs google).
Given that the two functions above are pure, we can further assert that any composition of these functions will also be pure, i.e.
const addThenMul = (a, b, c) => mul(add(a, b), c); const addThenSquare = (a, b) => { const c = add(a, b); return mul(c, c); }
Even without formal proof, this should be at least intuitive, if none of the subcomponents add side effects, then the component as a whole should not have side effects.
Since this relates to your problem, createConnection and createChannel are clean, there is really no need to simulate them, as their behavior is functionally controlled (as opposed to internal state). You can test them yourself to make sure they are working properly, but since they are clean, their composition (i.e. createConnection(amqlib, host).mergeMap(createChannel) ) will also stay clean.
Bonus
This property also exists in Observables. That is, the composition of two pure Observers will always be another pure Observer.