Jest: Rename jest.fn() to jest.spy()

Created on 3 Sep 2016  Â·  17Comments  Â·  Source: facebook/jest

I understand it’s a big change but should be super easy to find/replace if we do this.
Why:

  • One less global in code, relying on jest.fn() weird if you’re not entirely sold on Jest yet
  • jest.fn() is not descriptive at all, I had no idea what it means for a long time

Most helpful comment

OK. Can jest.fn() become jest.spy()? It’s just one more letter and way more descriptive.
We can change the docs to call them “spies” anytime too.

All 17 comments

It used to be called jest.genMockFunction() which was very confusing. In reality, this is used a lot and the longer it is, the fewer people use it and the more annoying it is. It litters the code a lot if it is longer too. I'll argue that expect is not really a better place for it.

My hope was that jest function becomes something that people think is synonymous with "mock function" or "stub function". I understand that jest.fn is more obvious to people that have used Jest for a long time (like engineers at Facebook) but I would like to take a revamp of our mock capabilities a lot further than just a rename: I want to build the best mocking library that's out there. We should come up with a perfect API for this and then go and build it.

We can also make a special fake module available in tests that gives you the jest runtime object. We can't really call it jest because then you cannot require jest inside of a test (like we do in Jest's own tests). What would we call such a module? require('âš’')? :D

_Note: jest is not technically a global, it is injected into the module scope and every module has a separate copy of the jest runtime object. This doesn't concern users, but it is nevertheless interesting._

The reason I’m saying it is as much as I like Jest, I just feel uncomfortable replacing expect.createSpy() in my code with jest.fn(). It feels wrong to use something implicitly injected called jest for something non-Jest-specific like creating a spy.

I can understand jest.mock() or jest.useFakeTimers() because those are Jest-specific features, but typing jest.fn() for every spy feels like Jest is trying to tie me down to itself.

I know it’s irrational, but programming is irrational in many ways. I’m sure I’m not the only one feeling uneasy because of such a silly reason.

Wait, is expect.createSpy a thing other frameworks use? In that case I'm happy to support it if its common.

It’s not commonly put on expect global but there’s a fairly popular expect library doing that.

expect in that case is the name of the library, though. If we were to look for a "pattern" in the wild, it would be to put the function on the global of the library. For example:

  • Sinon has sinon.spy() (174,607 GitHub code results) and sinon.stub() (120,488)
  • Jasmine has jasmine.createSpy() (378,392 GitHub code results)
  • Expect has expect.createSpy() (5,318 GitHub code results)
  • Chai Spies has chai.spy() (2,436 GitHub code results)

In Jest docs it's called mock function, not spy or stub, so jest.fn() practically follows this pattern already (jest.genMockFunction() really being too long name for a function that gets used so much). Renaming it to expect.createSpy also wouldn't make it work out of the box with code written for expect, because the signature is different: you can pass the function implementation to jest.fn: jest.fn(() => 3), whereas with expect you would do expect.createSpy().andReturn(3). So if you were to move to Jest mocks, you'd have to codemod that anyway.

Note also that if you want to port existing code gradually and not yet buy into all of Jest's built-in features, you can always use expect assertions and spies or sinon spies and stubs with Jest and it will work.

OK. Can jest.fn() become jest.spy()? It’s just one more letter and way more descriptive.
We can change the docs to call them “spies” anytime too.

I think jest.spy() would be a good name. It's short without being an abbreviation, spy is a common name for this functionality in other libraries, and even Jest docs already acknowledge it:

screen shot 2016-09-03 at 15 01 09

The reason I’d like to rename them to “spies” completely is because this makes the distinction between “mocking” feature (for module system, depends on Jest) and “spies” (for functions, not really Jest-specific) more clear.

Won't jest.spy get confused with jasmine.createSpy, which can also be used in Jest tests?

AFAIK none of jasmine APIs are officially supported in Jest and the plan is to eventually remove them. The official Jest API is here: https://facebook.github.io/jest/docs/api.html. The rest just happens to work.

I might be wrong on this though.

It's actually closer to a stub than a spy I think.

It's actually closer to a stub than a spy I think.

True - I don't think Jest provides an easy way to call the original implementation (ie. and.callThrough() in Jasmine), which is one of the things that differentiates spies from stubs.

To me, a spy is something that wraps around an existing function. We haven't added a helper for this yet, but I'd like to add

jest.spyOn(MyClass.prototype, 'onClick');

which would return a mock function. Therefore I'm unsure whether spy is really a good terminology for this.

In the end, all it is is really just a function that has a mock property with some data on it. Therefore, mock function and jest.fn seemed like a really convenient shortcut.

I do however understand Dan's concern about jest being a global. As mentioned before, we could make the jest runtime require-able but then it needs to have a unique name that isn't jest.

This is gonna be part of renaming jest-matchers to expect. I'll close this issue for now. https://github.com/facebook/jest/issues/1679

+1

+1

I know this is quite old and closed, but I just couldn't help myself.

but then it needs to have a unique name that isn't jest.

*drumroll*

jestr

Was this page helpful?
0 / 5 - 0 ratings