dce-stub
v1.0.9
Published
A simple way to stub default-only dependencies.
Downloads
10
Maintainers
Readme
dce-stub
A simple way to stub default-only dependencies. This project currently only works for stubbing the default export of a module.
We utilize dependency injection, so stubbed modules cannot be used until time of test (not at import time). For more info, see step 4 in the instructions below.
Usage
1. Import dce-stub
import runStubbed from 'dce-stub';
2. Import the dependencies you wish to stub using * syntax
In your spec:
import * as myDependency from '...';
...
In the module you are testing:
import * as myDependency from '...';
...
// When using the dependency, use the .default property:
myDependency.default...
...
3. Run your tests within the runStubbed
function
Call runStubbed
with two arguments:
- replacements
object|object[]
– either one or many stub replacements, each of the form{ dep, stub }
wheredep
is the dependency andstub
is the stubbed version of the dependency - test
function
– tests to run while the dependency is stubbed (dependencies are restored once the function finishes). Function may be asynchronous or synchronous
describe('My Feature', () => {
it('Does Something', async () => {
...
// Define stub replacement
const replacement = {
dep: myDependency,
stub: <stub of myDependency>,
};
// Run the
await runStubbed(replacement, async () => {
...
});
});
});
If you have more than one dependency to stub, just use a list:
...
// Define stub replacements
const replacements = [
{
dep: myDependency,
stub: <stub of myDependency>,
},
{
dep: secondDepencency,
stub: <stub of secondDependency>,
},
...
];
// Run the
await runStubbed(replacements, async () => {
...
});
4. Configure/use dependencies at time of use
We use dependency injection, which means that the stubbed version of the dependency is not injected at import time. Instead, it is injected when the test runs.
Don't configure/initialize/use stubbed libraries until time of test:
Example: we are using a library lms
that must be initialized before being used.
Wrong way:
import * as lms from 'my-lms'; const api = lms.default.getAPI(); class Course { constructor(id) { this.id = id; } listStudents() { return api.course.listStudents(this,id); } ... }
Right way:
import * as lms from 'my-lms'; class Course { constructor(id) { this.id = id; this.api = lms.getAPI(); } listStudents() { return this.api.course.listStudents(this,id); } ... }
Another right way:
(if there is no cost to re-initializing over and over)
import * as lms from 'my-lms'; class Course { constructor(id) { this.id = id; } listStudents() { const api = lms.getAPI(); return this.api.course.listStudents(this,id); } ... }
Note: One concrete example of such a library that can be re-initialized with no cost, is caccl/client/cached
Example
We have two helpers:
getNameFromServer
– pulls the current user's first name from the servergenIntro
– a script that callsgetNameFromServer
and generates a welcome message
Our goal is to write a unit test for genIntro
, isolating it from getNameFromServer
. Thus, we want to import genIntro
while replacing getNameFromServer
with a fake stubbed version of that module.
getNameFromServer.js
export default async () => {
return sendRequest({
method: 'GET',
url: 'https://host.com/user/profile/name',
});
};
genIntro.js
import getNameFromServer from './getNameFromServer';
export default async () => {
// Get the current user's name
const name = await getNameFromServer();
// Create the message
return `Hi ${name}! It is a pleasure to meet you.`;
};
genIntro.spec.js
import runStubbed from 'dce-stub';
// Import module to test
import genIntro from './genIntro';
// Import dependencies we want to stub
import * as getNameFromServer from './getNameFromServer';
// Create the getNameFromServer stub
const getNameFromServerStub = () => {
return 'Divardo';
};
// Tests
describe('genIntro', () => {
it('Generates a valid introduction', async () => {
// Create stub replacements
const replacement = {
dep: getNameFromServer,
stub: getNameFromServerStub,
};
// Run tests with stub replacements
await runStubbed(replacement, async () => {
// Generate an intro message
const intro = await genIntro();
// Test the intro
assert.equal(
intro,
'Hi Divardo! It is a pleasure to meet you.',
'Invalid intro produced'
);
});
});
});