Ninja Pumpkin Mutants

    @moxyjs/moxy

    0.1.0 • Public • Published

    MOXY

    Mock EVERYTHING with Proxy!

    Highlight

    • One simple but powerful API
    • Mock any function, object and class
    • Mock methods
    • Mock getters and setters
    • Mock any property chain optionally
    • Configurable & pluginable
    Index: Install - Usage - API - Advanced Topic

    Install

    with npm:

    npm install @moxyjs/moxy
    

    or yarn:

    yarn add @moxyjs/moxy
    

    Use with Jest

    Add the setup file in your jest config like:

    module.exports = {
      // ...
      setupFiles: [
        '<rootDir>/node_modules/@moxyjs/moxy/lib/extends/jest.js',
      ],
    };

    Usage

    import moxy from '@moxyjs/moxy';
    
    const spy = moxy(/* anything to mock */);
    
    spy(); // an empty function by default
    Mock a function
    const hello = moxy(() => 'world');
    hello('foo'); // 'world'
    
    hello.mock.fake(() => 'bar')
    hello('foo'); // 'bar'
    
    hello.mock.fakeReturnValue('baz')
    hello('foo'); // 'baz'
    
    expect(hello.mock).toHaveBeenCalledTimes(3);
    expect(hello.mock).toHaveBeenCalledWith('foo');
    Fake once
    const hello = moxy(() => 'world');
    hello.mock.fakeReturnValue('foo');
    
    hello.mock.fakeOnce(() => 'bar');
    hello.mock.fakeReturnValueOnce('baz');
    
    hello(); // 'bar'
    hello(); // 'baz'
    hello(); // 'foo'
    Mock an object
    const duck = moxy({
      say: () => 'quack',
      swim: true,
    });
    duck.say('foo'); // 'quack'
    duck.swim; // true
    
    duck.say.mock.fakeReturnValue('meow')
    duck.say('foo'); // 'meow'
    
    duck.mock.getter('swim').mockReturnValue(false)
    duck.swim; // false
    
    expect(duck.say.mock).toHaveBeenCalledWith('foo');
    Mock a class
    const Cat = moxy(class Cat {
      say() {
        return 'meow';
      },
    });
    const cat = new Cat('orange');
    cat.say('foo'); // 'meow'
    
    // the instance is mocked
    cat.say.mock.fakeReturnValue('purr');
    cat.say('foo'); // 'purr'
    
    // fake class implementation
    Cat.mock.fakeReturnValue(class NyanCat {
      say() {
        return 'nyan~nyan~nyan~';
      },
    });
    const cat2 = new Cat('rainbow');
    cat2.say('foo'); // 'nyan~nyan~nyan~'
    
    expect(Cat.mock).toHaveBeenCalledTimes(2);
    expect(Cat.mock).toHaveBeenCalledWith('rainbow');
    expect(cat.say.mock).toHaveBeenCalledTimes(3);

    API

    moxy(value, options)

    Return the mocked value

    • value - object|function, the obejct to be mocked, default to function(){}.
    • options - object, the mock options, default to {}
      • accessKey - string, the key to access Mock object, default to 'mock'
      • mockReturn: true - boolean, whether to mock returned value, default to false
      • mockNewInstance - boolean, whether to mock constructing call, default to true,
      • mockMethod:boolean, whether to mock methods, default to true,
      • recordGetter - boolean, whether to record getter calls, default to false,
      • recordSetter - boolean, whether to record setter calls, default to true,
      • middlewares - function[], middleware functions, default to null,
      • includeProperties - (string|symbol)[], mock matched methods and properties, default to null
      • excludeProperties - (string|symbol)[], exclude matched methods and properties, default to null

    Mock

    The mocking operator class

    • calls - Call[], the calling records
    • getter(key) - return the getter Mock of a property
    • setter(key) - return the setter Mock of a property
      • key - string|symbol - the property name
    • fake(impl) - fake function call
    • fakeOnce(impl) - fake function call once
      • impl - function, the faked implementation
    • fakeReturnValue(value) - fake returned value
    • fakeReturnValueOnce(value) - fake returned value
      • value - any, the faked value
    • wrap(wrapFn) - wrap function call behavior
    • wrapOnce(wrapFn) - wrap function call behavior once
      • wrapFn - (originalImpl) => fakedImpl, receive the original implementation and return the faked one
    • proxify(source) - return a mocked Proxy of the source which is controlled by itself

    Call

    A function call record

    • args - any[], the function call auguments
    • result - any, the returned value or the thrown error.
    • instance - any, the bound object, i.e. this
    • isThrow - boolean, whether the call is thrown
    • isConstructor - boolean, whether it's a constructing call with new

    isMoxy(value)

    Check whether a value is moxied. Return a boolean. For example:

    import moxy, { isMoxy } from '@moxyjs/moxy';
    
    isMoxy({}); // false
    isMoxy(moxy()); // true
    • value - any, the value to check

    factory(options)

    Create a moxy function with new default options. For example:

    import { factory } from '@moxyjs/moxy';
    const moxy = factory({
      recordGetter: true,
      mockReturn: true,
    });
    
    const foo = moxy();
    const bar = moxy();
    • options - object, the same as options of moxy

    Advanced Topic

    Mock an object deeply

    Any property chain matched by includeProperties is mocked deeply. The property name is checked using micromatch.

    const obj = moxy(
      {
        foo: {
          bar: {
            baz: {
              hello: () => 'world'
            },
          },
        },
      },
      { includeProperties: ['foo', 'b*'] },
    );
    obj.foo.bar.baz.hello(); // 'world'
    
    obj.foo.bar.baz.hello.mock.fakeReturnValue('there');
    obj.foo.bar.baz.hello(); // 'there'
    Use one Mock to mock many instances

    This is useful to mock all instances of a class:

    import { Mock } from '@moxyjs/moxy';
    
    const fooMock = new Mock();
    
    const Foo = moxy(class Foo {
      constructor() {
        this.bar = 'baz'
        return fooMock.proxify(this);
      }
    });
    new Foo().bar(); // 'baz'
    
    fooMock.getter('bar').fakeReturnValue('zaq');
    new Foo().bar(); // 'zaq'

    Or to mock a curried function:

    const mock = new Mock();
    mock.wrap((originalFn) => (...args) => {
      const nextValue = originalFn(...args);
    
      return typeof nextValue === 'function'
        ? mock.proxify(nextValue)
        : nextValue;
    });
    
    const curriedFn = mock.proxify(
      () => () => () => '🍛'
    );
    
    curriedFn('foo')('bar')('baz'); // '🍛'
    
    expect(mock).toHaveBeenNthCalledWith(1, 'foo');
    expect(mock).toHaveBeenNthCalledWith(2, 'bar');
    expect(mock).toHaveBeenNthCalledWith(3, 'baz');
    Proxy Handle Middleware

    You can define the underlying proxy handler with the middlewares option. For example:

    const foo = moxy({ bar: 'baz' }, {
      middlewares: [
        (handler) => ({
          ...handler,
          deleteProperty(target, prop) {
            target[prop] = 'deleted';
          },
        })
      ],
    });
    
    delete foo.bar;
    foo.bar; // 'deleted'
    Jest Expect Style

    If you don't like the .mock when making assertion, you can do this:

    // setup-jest.js
    const moxy = require('@moxyjs/moxy');
    const { attachJestFnProperties } = require('@moxyjs/moxy/jest');
    
    moxy.setDefaultOptions({
      middlewares: [attachJestFnProperties],
    });
    // jest.config.js
    module.exports = {
      setupFiles: ['<rootDir>/setup-jest.js'],
    };

    Then you can assert with the function directly, like:

    const foo = moxy();
    foo();
    
    expect(foo).toHaveBeenCalled();

    Keywords

    none

    Install

    npm i @moxyjs/moxy

    DownloadsWeekly Downloads

    4

    Version

    0.1.0

    License

    MIT

    Unpacked Size

    135 kB

    Total Files

    25

    Last publish

    Collaborators

    • lrills