@muban/test-utils v1.0.0-alpha.2
@muban/test-utils
This package contains a set of utility functions that makes testing code that interacts with Muban Components a lot easier.
The most obvious use case is testing hooks, but there might be other code that works with Components, or use things like refs to operate on.
Instead of trying to create real components in your tests, or try to construct these elements by hand, these functions will give you the tools to do that easily.
Additionally, it provides a getMubanLifecycleMock function that can be used as Jest Mock
to replace any @muban/muban import that uses the onMounted and onUnmounted Lifecycle Hooks.
Getting started
Installing
Add @muban/test-utils to your project:
npm i -D @muban/test-utils
yarn add -D @muban/test-utilsSetup Jest mocking
This is only needed when the code you're testing makes used of the
onMountedandonUnmountedLifecycle Hooks.
jest.mock('@muban/muban', () => jest.requireActual('@muban/test-utils').getMubanLifecycleMock());All other exports will be untouched, so you can import them as usual.
If your test code would need the actual onMounted and onUnmounted code for some reason, you
can use Jest's requireActual;
import type * as Muban from '@muban/muban';
// using `<typeof Muban>` makes sure that TS understands these imports
const { onMounted, onUnmounted } = jest.requireActual<typeof Muban>('@muban/muban');Api
getMubanLifecycleMock
Returns a mock object for the @muban/muban where the onMounted and onUnmounted are
replaced by an internal implementation (used by runComponentSetup).
This allows you to test Muban hooks outside of components (again, using runComponentSetup).
jest.mock('@muban/muban', () => jest.requireActual('@muban/test-utils').getMubanLifecycleMock());This code must be placed at the top of the file (below the import statements), since it should
execute before any other code. If you place this inside a test or function, it won't work.
runComponentSetup
A helper function that "fakes" how a component would run its setup and lifecycle methods.
It makes sure that the lifecycle hooks called during the setupFunction are bound to the
currentComponentInstance, so those hooks can be triggered.
Using the
runComponentSetupfunction for testing is only required when using lifecycle hooks. For other hooks, you can just execute and test them as any other functions.
async function runComponentSetup<T extends any>(
setupFunction: () => T,
runFunction?: (params: T) => void | Promise<void>,
): Promise<void>;Parameters
setupFunction()– A function to init your hooks. It runs before any lifecycle hooks are called, similar to the actual componentsetupfunction.runFunction(params)– A function that would simulate anything that would happen during a component's lifetime. Basically the user/dom that interacts with a component (e.g. clicking on it). This function can beasyncif you need this for your test case. It receives any parameters that are returned from thesetupFunction, so you can interact with anything that is returned from your hooks.
Returns
An empty Promise. Since the runFunction can be async, this whole function is async by default.
It's best to await this, and do you assertions after.
Example
const changeSpy = jest.fn();
it('should run a component lifecycle', async () => {
// run the `runComponentSetup`, and await in case it's async
await runComponentSetup(
// the `setupFunction` is similar to the `setup` of your component
() => {
// invoke the hook to test (which can have lifecycle hooks)
const state = useTest({
change: changeSpy,
});
// pass through some info to the `runFunction` to interact with
return { state };
},
// the usage of the `runFunction` is optional
({ state }) => {
// interact with the sate, to trigger logic in the hook
state.value = 'foobar';
},
);
// after the above is done, we can assert our logic,
// in this case, check if the spies are called with correct values
expect(changeSpy).toBeCalledTimes(2);
expect(changeSpy).toHaveBeenNthCalledWith(1, 'initial');
expect(changeSpy).toHaveBeenNthCalledWith(2, 'foobar');
});createMockElementRef
Creates a ref for an Element that can be used to pass to hooks or interact with in any other
way.
function createMockElementRef<T extends HTMLElement>(
element?: T,
): {
ref: ElementRef<T, BindProps>;
target: T;
};Params
element- An optional element to be used in the ref. When not passed, a div is created.
Returns
An object with a ref and a target
ref– The created reftarget– The html element for that ref
Example
const { ref, target } = createMockElementRef();
// this hook makes use of the `ref`
useSomeHook(ref);
// do test with this, or the result of the hook
expect(ref.element).toEqual(target);const element = document.createElement('span');
element.dataset.ref = 'foobar';
const { ref, target } = createMockElementRef(element);
expect(ref.element).toEqual(target);
expect(element).toEqual(target);
expect(target.dataset.ref).toEqual('foobar');createMockComponentRef
Creates a ref for an Component that can be used to pass to hooks or interact with in any other
way.
function createMockComponentRef<T extends ComponentFactory>(
component?: T,
template?: string,
): {
ref: ComponentRef<T>;
target: HTMLElement;
}Params
component- A component to be used in the ref. When not passed, a dummy one is created.template- An optional to be used for the passedcomponent. When not passed, a dummy one is created.
The use case for passing those overrides, is to access the parsed component props through the ref, or bind to the component props.
Returns
An object with a ref and a target
ref– The created ref linked to a test Componenttarget– The html element for that ref
Example
const { ref, target } = createMockComponentRef();
// this hook makes use of the `ref`
useSomeHook(ref);
// do test with this, or the result of the hook
expect(ref.component?.element).toEqual(target);const Demo = defineComponent({
name: 'demo',
props: {
foo: propType.number.defaultValue(42),
},
setup() {
return [];
},
});
const { ref, target } = createMockComponentRef(
Demo,
`<div data-component="demo" data-foo="69">test</div>`,
);
expect(ref.component?.name).toEqual(Demo.displayName);
expect(ref.component?.props.foo).toEqual(69);
expect(target.dataset.component).toEqual('demo');4 years ago
4 years ago