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

mock-fs in Promise callback not working? #253

Open
saitho opened this issue Sep 12, 2018 · 2 comments
Open

mock-fs in Promise callback not working? #253

saitho opened this issue Sep 12, 2018 · 2 comments

Comments

@saitho
Copy link

saitho commented Sep 12, 2018

I'm on Node v8.10.0 and I'm trying to use mock-fs for unit testing.
My controller removes a file using fs.unlinkSync which I'd like to check in a unit test.
I configured the file I want to delete in mock-fs. After the controller deleted the file I should be able to check with fs.existsSync if the file was deleted successfully. However I can't get mock-fs to work with Promises... (I'm not taling about fs.promises, #245) :(
fs.existsSync works as expected, but inside a Promise resolve callback function it doesn't. Any hints?

My test:

// ...
import * as mockfs from "mock-fs";
import * as fs from "fs";
// ...
it("updateAction - set new file name and delete old file", (done) => {
        const data = {pdfFileName: 'oldFile.pdf'}
        mockfs({
            'some/directory/': {
                'oldFile.pdf': 'file-contents',
            },
        });
        console.log('before action: ' + fs.existsSync('some/directory/oldFile.pdf')); // is true
        new Controller().updateAction(1, {pdfFileName: 'newFile.pdf'}, 'some/directory')
            .then((value) => {
                console.log('after action: ' + fs.existsSync('some/directory/oldFile.pdf')); // is false
                done();
            })
            .catch((e) => done(e));
        mockfs.restore();
    });

Controller:

// ...
import * as fs from "fs";
// ...
public updateAction(id: number, data: any, directoryPath = '') {
        return new Promise<void>((resolve, reject) => {
            console.log('during action: ' + fs.existsSync('some/directory/oldFile.pdf')); // returns true
            this.getRepo().findOneById(id)
                .then((result: any) => {
                    console.log('during action: ' + fs.existsSync('some/directory/oldFile.pdf')); // returns false
                    if (result.pdfFileName !== data.pdfFileName) {
                        // remove old file
                        const filePath = path.join(directoryPath, invoice.pdfFileName);
                        if (directoryPath && fs.existsSync(filePath)) {
                            fs.unlinkSync(filePath);
                        }
                    }
                    // ... save operations etc.
                    resolve();
                })
                .catch((error) => reject(new DatabaseError(error)));
        });
    }
@saitho saitho changed the title mock-fs in Promises not working? mock-fs in Promise callback not working? Sep 12, 2018
@maxwellgerber
Copy link
Contributor

I can't seem to reproduce - if you can post a minimum example I'll take another look.
The following works as expected:

'use strict';

const mock = require('mock-fs');
const fs = require('fs');

const del = path => {
  console.log(fs.existsSync(path)); // true
  return Promise.resolve()
    .then(() => {
      console.log(fs.existsSync(path)); // true
      if (fs.existsSync(path)) {
        fs.unlinkSync(path);
      }
    });
};

mock({
  'some/directory/': {
    'oldFile.pdf': 'file-contents',
  },
});

const path = 'some/directory/oldFile.pdf';
(async () => {
  console.log(fs.existsSync(path)); // true
  await del(path);
  console.log(fs.existsSync(path)); // false
})();

@gavinvangent
Copy link

gavinvangent commented Mar 19, 2019

I believe the problem is that you are calling restore outside of your promise chain:

new Controller().updateAction(1, {pdfFileName: 'newFile.pdf'}, 'some/directory')
  .then((value) => {
    console.log('after action: ' + fs.existsSync('some/directory/oldFile.pdf')); // is false
    done();
  })
  .catch((e) => done(e));
  
  mockfs.restore();

The reason is that the promise chain hasn't executed by the time you are calling restore, so restore is actually being invoked before fs.unlinkSync(filePath); in the controller method

to fix it, use this:

new Controller().updateAction(1, {pdfFileName: 'newFile.pdf'}, 'some/directory')
  .then((value) => {
    console.log('after action: ' + fs.existsSync('some/directory/oldFile.pdf')); // is false
  })
  .catch((e) => e)
  .then(e => {
    mockfs.restore();
    done(e)
  })

the fix I'd most recommend is this:

import * as mockfs from "mock-fs";
import * as fs from "fs";
// ...

afterEach(() => {
  mockfs.restore();
})

// ...
it("updateAction - set new file name and delete old file", () => {
  const data = {pdfFileName: 'oldFile.pdf'}
  mockfs({
    'some/directory/': {
      'oldFile.pdf': 'file-contents',
    },
  });
  
  console.log('before action: ' + fs.existsSync('some/directory/oldFile.pdf')); // is true

  return new Controller().updateAction(1, {pdfFileName: 'newFile.pdf'}, 'some/directory')
    .then((value) => {
      console.log('after action: ' + fs.existsSync('some/directory/oldFile.pdf')); // is false
      // do all your asserting
    })
});

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

No branches or pull requests

3 participants