question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

RFC: Function Mocks

See original GitHub issue

This issue facilitates discussions about builtin function mocks design. First of all, we believe that using mocks is so common that making them part of the library is a sane thing to do.

Sinon’s Spy

const spy = sinon.spy(() => 5)

// and for asserts with chai-sinon:
expect(mySpy).to.have.been.calledWith("foo");

What I don’t like about this design is that it doesn’t have support for easy creation of a spy that returns different values for different calls.

Another thing is that I always felt like there is way too many sinon-spy matchers but some really useful were missing.

Jest’s fn.mock()

const mock = jest.fn(x => 42 + x)
expect(mockFunc).toHaveBeenCalledWith(arg1, arg2);

What I like about its design:

  • clear design of .mock property exposing all the details - makes it possible to write custom expectations by hand,
  • Simple helpers to mock different values for different calls:mock.mockReturnValueOnce(‘x’)

Proposed design

In my experience, I quite often want to return different values for different calls, thus something like Jest’s mock.mockReturnValueOnce (internal queue of returned values) is a must. But this leads us to this weird state where we define returned values in one place but write expectations in a totally another. What if we combined it to something like this:

import {mockFn} from "earl"

const getSizeMock = mockFn([{inputs: [expect.a(Fs), "/path/file.txt"], out: 217}])

This mock expects to be called exactly once with a object of type Fs and string /path/file.txt and returns 217.

There are at least few cool things about this:

  • can throw expectations as soon as unexpected call happens,
  • inputs is just an array of args so one can use all matchers that would work with beEqual (arrayContaining if you don’t care about details, or anything if you really don’t care ;d). This API design is just an example, we could utilize helpers like mockReturnValueOnce etc but I wanted to present here that I believe that expectations on a mock can be defined as a part of a mock.

Furthermore, it should be possible to do something like expect(mock).toBeExhusted() to verify if a mock’s internal queue of values to return is empty. So there is no need for something like expect(mock).toHaveBeenCalledTimes(5).

I imagine that this could be even further simplified with optional integration with test runner (this needs another proposal) which would ensure all mocks created in a testcase are exhausted after each test run.

There is only one thing that I don’t like about this idea - implementing autofix (for inputs) will be challenging as expectations are defined first and execution happens later.

To clarify, this could be preferred way of using mocks but for sure there are cases where more traditional interface like:

const mock = jest.fn(x => 42 + x)
expect(mockFunc).toHaveBeenCalledTimes(3);

is needed and thus should be supported as well.

Thoughts?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:11 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
krzkaczorcommented, May 14, 2020

Yesterday I dove into various mocking frameworks outside JS to steal some good ideas. I even visited PHP land - but I couldn’t find any good patterns there 😆

Good news is that it turns out that I didn’t invent the concept of strict/loose mocks 😆

Here’s what I propose:

  1. strictMocks (preferred) - functions with a sequence of expected calls and return values specified UPFRONT. They will throw on unexpected calls. They should be always verified by the end of the test (can be done automatically by integration with test runner TBD).

  2. mocks (or looseMocks) - functions with dummy implementation/return value, that you can call as many times as you want. They have nice API to override return values for given args. At the end of the test can be verified by set of custom matchers.

(2) is much more similar to what sinon.spy / jest provides but I feel like (1) is be more useful IRL (at least this is how I often use mocks). I also propose to implement (1) in the first place.

2reactions
sz-piotrcommented, May 12, 2020

How about a system of overrides that allows you to modify things at a later date?

const callMeMaybe = mockFn().returns(3)
expect(callMeMaybe()).toEqual(3)

callMeMaybe.throwsOnce(new Error('booyah'))
expect(() => callMeMaybe()).toThrow('booyah')
expect(callMeMaybe()).toEqual(3)

callMeMaybe
  .given(1, 3).returns(4)
  .given(2, 4).returns(6)

expect(callMeMaybe()).toEqual(3)
expect(callMeMaybe(1, 3)).toEqual(4)
expect(callMeMaybe(2, 4)).toEqual(6)

callMeMaybe.reset().returns(5)

expect(callMeMaybe()).toEqual(5)
Read more comments on GitHub >

github_iconTop Results From Across the Web

RFC: Mocking · Issue #197 · colinhacks/zod - GitHub
I'm open to a PR for a .mock method that lets you generate mock data for your schemas. Proposal. New base method: .mock....
Read more >
[RFC,v1,21/31] kunit: mock - Patchwork
Up to this point KUnit only supported method style function mocking where there was some type of class or context object and the...
Read more >
[RFC v1 21/31] kunit: mock: added support for ... - Linux-Kernel Archive
Up to this point KUnit only supported method style function mocking where there was some type of class or context object and the...
Read more >
Can a JCo server simulate a SAP remote function?
Now, of course it's possible to create a JCo server program, which provides logic for handling an RFC function call (i.e. the mock-up...
Read more >
How to write nUnit test cases for SAP RFC calls - CodeProject
I want to mock these RfcDestination method calls and fetch the default data, Please let me know how to proceed.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found