Closed mjackson closed 7 years ago
@mjackson i think the biggest issue will be migrating expect(x).toMatchSnapshot()
and we haven't quite figured it out ourselves yet :)
the issue here is that it depends on some jest global state (like current running test, filename, whether it's running in --update
mode or not, etc). and that means that there has to be some pretty complicated integration with the test runner.
and also
whenever something doesn't match snapshot, we don't really throw an error right away. Instead, we log the failure and decide what to do with it after the test finishes.
@dmitriiabramov Help me understand. Right now, expect
doesn't have any concept of snapshotting. So it's essentially just a new feature for expect
users. What is there to migrate?
Coupling this to the global state of a specific runner for snapshots would make this a non-starter. I'm also confused why nothing would be thrown immediately (also seems like that would break usage in other test runners).
@phated It's my understanding that expect(x).toMatchSnapshot
depends on global state, but the other methods do not and will continue to throw immediately. Since toMatchSnapshot
isn't actually something that currently exists in expect
, it shouldn't adversely affect existing users.
We won't be adding the snapshot matcher to expect from the beginning. For now it will just be jest-matchers
. Snapshotting is done in its own package, jest-snapshot
. We will explore ways to bring snapshotting to other runners through expect
(cc @suchipi) but it isn't our immediate goal. @dmitriiabramov didn't know this was coming because I failed to communicate with him.
I don't know how expect
is architected, but in https://github.com/facebook/jest/pull/1413 I was trying to make the snapshot matcher pretty agnostic; with the proposed changes, this is how it would be used:
import {createSnapshotState, processSnapshot} from 'jest-snapshot';
const fileName = "/home/whoever/whatever.snap.js";
const snapshotName = "renders correctly 1";
const actual = {/* ... */};
const options = {updateSnapshot: true}; // or false
const snapshotState = createSnapshotState(fileName);
const {pass, expected} = processSnapshot(snapshotState, snapshotName, actual, options);
So as long as you can bring your own actual
, path to the snapshot file, and whether it should update, you could implement it anywhere.
Small update: we just added expect.extend
:) We are working on making this happen!
@cpojer woo! That was definitely one of the largest differences. Is there a branch I can try out? Or did you put it in a release already? I'll make some time soon to check out jest-matchers
and see what's still needed to make this happen so we can have a clear roadmap.
Hey @mjackson,
we are tracking this internally right now. The JS Tools team is pretty busy until the end of year with our three projects (Yarn, Jest and react-native-packager) so we may not make a ton of progress on this immediately but it's definitely one of our goals to make this work well. I'll share something more soon :)
@mjackson FWIW after using both the chai style .not.equal
and expect .toNotEqual
, I always preferred the mjackson/expect style better. I know the code is probably simpler the other way, but I prefer less dots. Here is an even worse example from chai expect('foo').to.have.length.of.at.least(2);
. Painful to type (for me). I don't even like how it looks/reads either. Just my opinions of course, I'm sure there are others who like the dot style.
I agree, Jeff. I don't like the chai style either. We're moving to the Jasmine style. So .not.toEqual instead of .not.to.equal.
See the difference? On Tue, Nov 15, 2016 at 11:21 PM Jeff Barczewski notifications@github.com wrote:
@mjackson https://github.com/mjackson FWIW after using both the chai style .not.equal and expect .toNotEqual, I always preferred the mjackson/expect style better. I know the code is probably simpler the other way, but I prefer less dots. Here is an even worse example from chai expect('foo').to.have.length.of.at.least(2);. Painful to type (for me). I don't even like how it looks/reads either. Just my opinions of course, I'm sure there are others who like the dot style.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/jest/issues/1679#issuecomment-260850514, or mute the thread https://github.com/notifications/unsubscribe-auth/AAFqp4Xzrs2nOqdO0hFBdFlj2rsMSOo-ks5q-oTOgaJpZM4J63Xy .
Michael Jackson @mjackson
I believe there is value in the not
negator but any more dots to chain matchers is not really useful imho. I understand this is subjective but I believe the current style in Jest is clean and straightforward. We can also build simple modules that will bring back things like toNotEqual
because it's implementation is basically just expect(foo).not.toEqual()
.
@mjackson yes, I agree that wouldn't be too bad if we are just adding one dot clause. @cpojer I like that idea too, I'd certainly load up that extra module to keep the old style :-) Thanks for your consideration. I know it isn't life or death decision, but it does influence the developer experience. :-)
Absolutely, it affects the UX. Thanks for chiming in here and caring, Jeff :)
FWIW, I think that .not
is the best UX for both plugin authors and users.
For plugins, authors only have to write one assertion instead of two. So, e.g. if I want to make a toFlyLikeABird
assertion, I don't have to make a toNotFlyLikeABird
version as well. .not
gives me the negated version for free.
For users, I only have to remember one form of the assertion instead of 2.
I would also like to add that I use jasmine API sometimes, like spyOn that will be covered with jest once this comes out, but there's one more example where I use jasmine:
expect(someModule.someFunction).toHaveBeenCalledWith(arg1, arg2, jasmine.any(Function));
Because we are also passing anonymous callback function as 3rd parameter we have to expect it because .toHaveBeenCalledWith function will fail if we try to expect only first 2 arguments without selecting last one. I don't mind using jasmine, just giving an example for you guys to decide if it's worth covering this with jest.
@vjeux is helping out to make this a reality.
Here is what was done so far, among adding a ton of features to jest-matchers
:
jest-matchers
.jest-matchers
to export expect
directly: #2391.TODO
expect.assert
doesn't really make much sense because node comes with assert
. I'd rather not add it and include it in the codemod.spyOn
to Jest: we are currently unsure whether it should be a separate module (jest-mock
) or whether these should be on expect
. I would prefer to make this a breaking change and have the codemod take care of it. Is this ok @mjackson?Assuming we'll figure out these things soon, are you happy to transfer ownership of the project to us?
I forget if this has been brought up but can this remain es5-compatible?
(edit): We'll likely support the syntax that is supported in node 4 and up. You can add an additional transformation step for the environment you are using.
Also, the current expect library is great and works well. If you've been happy about using it in the past and you don't want to upgrade on existing projects, I think that's fine too.
Here's an example of the before/after
jest-matchers:
current expect:
It doesn't make much difference, but my vote for spyOn
would be towards jest-mock
, just because "expect.spyOn
" sounds kinda weird
Awesome, thanks @vjeux! :D
expect.assert doesn't really make much sense because node comes with assert. I'd rather not add it and include it in the codemod.
expect
currently works in non-node environments. Is jest-matchers
currently node-only? I can understand Jest only running in node, but I don't see any reason why an assertion lib should depend on it.
Can you provide us with an integration test? How does expect work in the browser? Do you ship a single build file of it or do you let webpack/browserify take care of the bundling? There is nothing that should prevent jest-matchers to work in the browser (assuming chalk
is shimmed properly) but we haven't tested it yet.
cc @zertosh who knows how to create bundles for webpack. Can you help us get a build file of the "jest-matchers" package?
@mjackson Jest is also running in jsdom
Sorry, clicked the "Comment" button too soon.
Add spyOn to Jest: we are currently unsure whether it should be a separate module (jest-mock) or whether these should be on expect. I would prefer to make this a breaking change and have the codemod take care of it.
spyOn
is just sugar over createSpy
. The current implementation is just a few lines. I've found this API useful for doing things like
const obj = {
func: () => {
// ...
}
}
const spy = expect.spyOn(obj, 'func').andCallThrough()
// call it
obj.func()
// make your assertions
expect(spy).toHaveBeenCalled()
// teardown
spy.destroy()
Assuming we'll figure out these things soon, are you happy to transfer ownership of the project to us?
Of course! :D
Yeah, we are of course happy to support spy
and spyOn
but I think it is better to keep them separate inside of jest-mock
as mock.fn
and mock.spyOn
. expect.spy
and expect.fn
doesn't really work that well imho and I'd prefer not to add too much to expect, it's already a lot.
Do you ship a single build file of it or do you let webpack/browserify take care of the bundling?
I currently do both for all of my projects. UMD builds are good for things like codepens. But I also make sure everything works if you just require
it using a bundler.
My current webpack config is pretty straightforward and just performs the babel transform.
Also, I have the feeling that this is kind of like the clash of two different development philosophies. You guys may not ever need to actually run your tests in a non-node environment, which is fine. I can pitch in and do the work to make stuff work in the browser if needed. Just NEED MOAR TIME ;)
FWIW I've been using expect
in online courses on JSBin a lot.
@gaearon you can use Jest in the browser via repl.it: https://repl.it/languages/jest
@mjackson if you can send a PR to Jest that bundles up jest-matchers, that would be great. If there are any issues for running the browser, we'll obviously fix them but I don't expect any real issues once we have a bundle. I'd like to switch the name over to expect in early January.
@cpojer My content for Egghead includes specifically JSBin links because they are integrated, adding another third party service to the mix is going to be confusing. It doesn't matter for me (although expect@latest
will likely break and I need to replace it) but just saying other people may use it in training materials too and might not want to use replit for one reason or another.
@mjackson, @gaearon do you have examples of codebases using expect? I'm writing a codemod to convert to jest-matchers and would like to try it.
In addition to the router, you should be able to just npm install
and npm test
almost any of my stuff:
Done:
spyOn
and fn
to jest-mock
as a standalone module. Within Jest, jest.spyOn
and jest.fn
are available.jest-matchers
is now standalone and works in the browser.Todo:
expect.spyOn
to jestMock.spyOn
yet.jest-matchers
to expect
.I think we are ready to cut over any day now :)
Great news! So if I clone this repo and npm link
jest-matchers
, should I expect it to work more or less like expect
currently does? i.e. can I
import expect from 'jest-matchers'
expect(stuff).toWork()
? Just wondering because I'm a Jest newb and I wanna give it a go this week. I'd like to convert all my projects to jest-matchers
first, and then go full-on jest
at some point in the future when I have time to do the code mods and refactor my test suite.
Here is the process:
yarn add --dev jest-matchers jest-mock
jscodeshift -t path/to/expect.js <folder or files to transform>
expect.createSpy
with const mock = require('jest-mock'); mock.fn()
and expect.spyOn
with mock.spyOn
. Note: we made a behavior change here: by default, spyOn calls the original implementation (that's why it is a spy). If you don't want that, use mock.spyOn(…).mockImplementation(() => {})
or `obj[method] = mock.fn().. If you are using Jest, then you can use jest.fn
instead of mock.fn
.When hitting errors, take a note on what doesn't work and list them here for us so that we can either make a decision not to support it (breaking change) or update the codemod. Then advise on the next steps please.
I recommend the following:
jest-matchers
to expect
and publish a release (it would be version 19.x.x).expect
instead of jest-matchers
.Since the current version of the expect package is kind of at the end of life, if people aren't willing to upgrade to the new version of expect, there isn't really any issue with people staying on the old version. It works and is fine.
Hmm, I'm running into problems when trying to use jest-matchers in the browser, it is requiring console
package which is made for node environment.
Here are some additional changes that I have come across
expect.spyOn(...).andCall(fn)
with jest.spyOn(...).mockImplementation(fn)
expect.spyOn(...).andReturn(value)
with jest.spyOn(...).mockImplementation(() => value)
expect.createSpy().andCall(fn)
with jest.fn().mockImplementation(fn)
expect.createSpy().andReturn(value)
with jest.fn().mockImplementation(() => value)
andThrow(error)
-> mockImplementation(() => throw error)
spy.calls[i].arguments[j]
-> spy.mock.calls[i][j]
spy.calls.length
-> spy.mock.calls.length
I think expect.restoreSpies()
functionality is missing from jest
Hey @skovhus, you are a codemod pro. Do you think you could help us out here by taking https://github.com/cpojer/js-codemod/blob/master/transforms/expect.js and putting it into jest-codemods and adding a few of the fixes that @kentor just mentioned?
@jpojer thanks, would love to add that to Jest Codemods. On vacation this week, but will look into it next week. ; )
FYI spyOn
seems to have been added with #2537.
Happy to say that the transformer for this in Jest-Codemods is almost there (see progress in https://github.com/skovhus/jest-codemods/pull/39). Think it will get most projects 95% of the way when transitioning from expect@1 to Jest/Jest-matchers.
Trying it out on a few ReactTraining repos, shows that we got some ES2015 in jest-matchers
. This breaks the tests as jest-matchers
is used in older browser. See https://github.com/facebook/jest/issues/3360
@cpojer @SimenB any recommendation how expect.restoreSpies()
should be code modded?
(related to https://github.com/facebook/jest/issues/2965)
@skovhus if it isn't supported right now, we should add that API to jest-mock
. Happy to accept PRs.
I think resetAllMocks
is what you're after. It restores stuff that's been called with jest.spyOn
. Or do you think it's too broad (since it takes all spies/mocks that ever were)?
PR for it: #4345
@gaearon You can load the new Expect library (as maintained by facebook/jest
) using the awesome service unpkg. Just use the URL https://unpkg.com/expect@latest/build-es5/index.js
, which resolves to the latest version 22.1.0
(as of 23.01.2018). Hope that helps! :smile:
jest-mock
is also available in a build-es5
directory if you need that
@gaearon Proof of concept of running jest/expect
inside JS Bin: jsbin/nucimeceve.
I decided to wrap the expect
package from facebook/jest
as a standalone UMD module. It is located at jest-expect-standalone.
Include jest-expect-standalone
as a script tag, and you will have the library available via window.expect
.
<script src="https://unpkg.com/jest-expect-standalone@latest/dist/expect.min.js"></script>
See sample JS Bin jsbin/wapokahaxe.
@gaearon Heads up!
I'd like to donate use of the expect npm package to the Jest project. We're both working on very similar
expect
-style APIs, except you guys are killing it with awesome error messages and I'm not. So I wanna let you run with it :)AFAICT there are only a few places where our APIs differ. Please correct me if I'm wrong. They are:
expect
uses e.g..toNotEqual
instead of.not.toEqual
. TBH I like the.not
notation better. Otherwise you end up creating a negated form of every single assertion. So this is đź‘Ť . I'm fine w breaking backwards compat. Maybe we can provide a codemod for people who want to automate the upgrade process. Should be a simples/toNot/not.to/g
.expect
includes support for mocha's error diffing when usingtoEqual
(see here). I don't expect full mocha support, but I'd like to keep at least this one aspect for people currently usingexpect
with mocha to help make the transition to Jest easier.expect
exposes its basic assert function for those who just need toexpect.assert(something)
. I don't see this in Jest. Would it be easy to expose this?expect
includes anexpect.extend
method for extending the core assertions. We use it e.g. inexpect-element
to generate assertions specific to DOM nodes andexpect-jsx
for JSX. Inexpect
we just add properties toExpectation.prototype
. Does Jest have a method for doing something similar?expect
has aexpect.createSpy
API where you usejest.fn
.expect.createSpy
could just be a simple alias forjest.fn
.expect
has anexpect.spyOn
function. I don't see this in Jest, but it should be pretty easy to keep it. It's just a wrapper forcreateSpy
.expect
have acalls
property that is an array of{ context, arguments }
objects. Jest usesmock.calls
that is an array of arguments. I'm fine with deprecating and using Jest's API here. Just something to note for people upgrading.Overall I think this should be fairly easy from my side. Not sure what this looks like from your side though. Anything I missed?
/cc @cpojer