Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jest-plugin-fs] Add support for spyfs to fs plugin #74

Open
streamich opened this issue Nov 2, 2017 · 7 comments
Open

[jest-plugin-fs] Add support for spyfs to fs plugin #74

streamich opened this issue Nov 2, 2017 · 7 comments

Comments

@streamich
Copy link

Hi,

Do you think the fs plugin could also make use of spyfs package to mock fs and intercept fs requests in flight?

Description

Screenshot

Test Plan

@negativetwelve negativetwelve changed the title spyfs for fs plugin [jest-plugin-fs] Add support for spyfs to fs plugin Nov 2, 2017
@negativetwelve
Copy link
Owner

Hi @streamich interesting concept. How could this be used in a testing scenario? Do you have an example of what the API would look like for the jest-plugin-fs plugin?

@streamich
Copy link
Author

streamich commented Nov 2, 2017

You could export a spyfs object that allows to mock, intercept and listen to file system operations.

import fs, {spyfs} from 'jest-plugin-fs';

jest.mock('fs', () => require('jest-plugin-fs/mock'));

describe('FileWriter', () => {
  it('should handle error gracefully', () => {
    spyfs.on('openSync', action => action.reject('Open failed'));

    FileWriter.doSomething();
  });
});

@negativetwelve
Copy link
Owner

@streamich sorry for the late reply. How would assertions look in this case? Why can't I just use a mock on the fs object to listen for specific methods being called?

@streamich
Copy link
Author

streamich commented Nov 9, 2017

You can, but it is not just about listening for mock being called. You can also implement a mock or fail some method.

Also if you mock a method it will no longer do the actual function, at least by default.

Let's say you want to test some method opens /file.txt asynchronously. With spyfs right now it could be something like this:

import fs, {spyfs} from 'jest-plugin-fs';

it('should open /file.txt', () => {
  openFileTxt();
  return new Promise((resolve) => {
    spyfs.on('open', action => {
      expect(actions.args[0]).toBe('/file.txt');
      resolve();
    })
  });
});

This is probably not the most elegant example, but something like that. How would you check if some file was opened using current setup.

With a bit of work we could simplify it further:

import fs, {spyfs} from 'jest-plugin-fs';

it('should open /file.txt', () => {
  openFileTxt();
  return spyfs.once.open({args} => expect(args[0]).toBe('/file.txt'));
});

In this example spyfs.once.open would return a Promise that listens only for the next immediate call to fs.open, and the callback inside it has the standard spyfs semantics, like it receives the action object.

@negativetwelve
Copy link
Owner

This is interesting and I like where this is going! For the second example you provided, I would expect a custom matcher that achieves what you're doing. It could use spyfs under the hood but I'm thinking the syntax for the test would be something like:

it('should open /file.txt', () => {
  expect(openFileTxt).toOpenFile('/file.txt');
});

it('should write once to /file.txt', () => {
  expect(writeOnceFileTxt).toWriteOnceToFile('/file.txt');
});

Does that make sense? These custom matchers would utilize spyfs under the hood.

@streamich
Copy link
Author

streamich commented Nov 9, 2017

I like the idea of custom matchers.

Is custom matchers something supported by Jest? Or we can just bolt them on expect method?

I'm thinking it might have to be a callback, just like when using .toThrow() assertion:

it('should open /file.txt', () => {
  expect(() => openFileTxt()).toOpenFile('/file.txt');
});

Also would be nice to somehow chain the assertions:

it('something /file.txt', () => {
  const expected = expect(() => openFileTxt());
  expected.toOpenFile('/file.txt');
  expected.toWriteOnceToFile('/file.txt');
});

// or

it('something /file.txt', () => {
  expect(() => openFileTxt())
    .toOpenFile('/file.txt')
    .toWriteOnceToFile('/file.txt');
});

@negativetwelve
Copy link
Owner

yep! supported and encouraged: https://facebook.github.io/jest/docs/en/expect.html#expectextendmatchers

Yeah, it's a callback, the example I posted above was treating the function as a callback. () => openFileTxt() is the same as openFileTxt in this case

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants