This is just a brief write-up of why you might want to use hanbi and how.
What is it?
When writing tests in JavaScript, it is inevitable that you'll eventually want to mock/stub out some functionality in your code.
hanbi is one of the many solutions to this - a small library for stubbing out and spying on functions.
Why?
Most people these days immediately install their go-to testing stack without a second thought when creating a new repository. Often, this of course means installing "sinon".
It is a good library, but it has grown over the years to become very bloated... trying to solve everyone's problems in one go and trying to cover every possible use case.
Just a few of the reasons I wanted to move off it:
- It is hundreds of KiB in size (not such a problem as a dev-dependency, but does not scale well in CI)
- It tries to be too helpful (
calledThrice
is too far) - It doesn't play nice with TypeScript (the types work, but they are far too over-complicated and have to be)
For these reasons and more, I just wanted a light alternative which was focused on only handling stubs/spies. No extra help, no 'magic', no shortcuts. Just a focused base implementation.
Some advantages
I love focused libraries which do less but do it well. Examples are esbuild, rollup, prettier and so on.
Hanbi is just the same, very focused with an aim to stay that way.
It is also written in TypeScript, and in a way which means its types work very smoothly and logically. Less crazy types = better.
How to use it
Using hanbi is incredibly simple.
It provides us a way to create a "spy" which monitors its own calls, and a way to create a "stub" which effectively replaces some function with a spy.
We can install it pretty easily:
$ npm i -D hanbi
Spying
For when we want to track the calls of an anonymous function (like an event handler), we can simply create a "spy".
These spies can then be passed in to whatever functionality you're trying to test as if they were their "real" counterparts.
const spy = hanbi.spy();
window.addEventListener('load', spy.handler);
spy.called; // true once the event fires
This is useful when we need to pass in a callback function, test an event handler or something similar.
Stubbing
For when we need to mock out a piece of functionality which we are expecting to trigger, we can stub it.
For example, preventing an object from sending an actual request off or similar.
class Foo {
myMethod() {
return 5;
}
myOtherMethod() {
return this.myMethod();
}
}
const instance = new Foo();
const stub = hanbi.stubMethod(instance, 'myMethod');
instance.myMethod(); // undefined
instance.myOtherMethod(); // undefined
stub.returns(6);
instance.myMethod(); // 6
instance.myOtherMethod(); // 6
stub.called; // true
Assertions/state
There are a few main assertions and pieces of state you can use on a spy/stub:
const spy = hanbi.spy();
// Gets a specific call
const call = spy.getCall(2);
call.args; // The arguments passed when it was called
call.returnValue; // The value that was returned
call.thisValue; // The `this` at the time it was called
// Determines if the spy was called
spy.called;
// Determines if the spy was called with specific args
spy.calledWith('arg1', 'arg2');
// Determines if the spy ever returned a specific value
spy.returned('foo');
Wrap-up
Again, this isn't meant to be taking on all those existing testing libraries you make use of today. I still contribute to sinon and keep its TypeScript types well maintained when I can.
This is just a far more focused alternative for anyone interested.
I came up with it primarily to solve a problem I had: sinon was slowing down CI builds to a noticeable amount, and was only used for a fraction of its functionality.
If you give it a try, do let me know your thoughts and feel free to make any suggestions in the GitHub issues.
Top comments (0)