jest-plugins icon indicating copy to clipboard operation
jest-plugins copied to clipboard

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

Open streamich opened this issue 8 years ago • 7 comments

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

streamich avatar Nov 02 '17 00:11 streamich

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?

negativetwelve avatar Nov 02 '17 00:11 negativetwelve

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();
  });
});

streamich avatar Nov 02 '17 10:11 streamich

@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?

negativetwelve avatar Nov 09 '17 09:11 negativetwelve

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.

streamich avatar Nov 09 '17 10:11 streamich

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.

negativetwelve avatar Nov 09 '17 11:11 negativetwelve

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');
});

streamich avatar Nov 09 '17 12:11 streamich

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

negativetwelve avatar Nov 09 '17 14:11 negativetwelve