question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Server does not complete setup before next test suite executes in Mocha

See original GitHub issue

Environment

Name Version
msw 0.27.0
node 12.19.0
OS Linux x86_64

Request handlers

Note: I have created a repo displaying the behavior here.

App.test.js

import React from 'react';
import { render } from '@testing-library/react';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import App from '../App';

describe('App tests', () => {
  const server = setupServer(
    rest.post('http://localhost:8080/api/getList', (req, res, ctx) => {
      return res(
        ctx.set('access-control-allow-origin', '*'),
        ctx.json({
          msgCode: 0,
          msgDesc: 'Request processed successfully',
          data: {
            data: {
              createdBy: 'CJ',
              createdDate: '02/18/2021',
            },
            pagination: {
              pageNo: req.body.pagination.pageNo,
              pageSize: req.body.pagination.pageSize,
              total: 1,
              sortedColumn: req.body.pagination.sortedColumn,
              sortedType: req.body.pagination.sortedType,
            },
          },
        })
      );
    })
  );

  before(() => {
    server.listen();
  });

  after(() => {
    server.close();
  });

  it('renders App without crashing', () => {
    render(<App />);
  });
});

Child.test.js

import React from 'react';
import { render } from '@testing-library/react';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import Child from '../Child';

describe('Child tests', () => {
  const server = setupServer(
    rest.post('http://localhost:8080/api/getChildList', (req, res, ctx) => {
      return res(
        ctx.set('access-control-allow-origin', '*'),
        ctx.json({
          msgCode: 0,
          msgDesc: 'Request processed successfully',
          data: {
            data: {
              createdBy: 'CJ Child',
              createdDate: '02/18/2021',
            },
            pagination: {
              pageNo: req.body.pagination.pageNo,
              pageSize: req.body.pagination.pageSize,
              total: 1,
              sortedColumn: req.body.pagination.sortedColumn,
              sortedType: req.body.pagination.sortedType,
            },
          },
        })
      );
    })
  );

  before(() => {
    server.listen();
  });

  after(() => {
    server.close();
  });

  it('renders Child without crashing', () => {
    render(<Child />);
  });
});

Actual request

The actual request is simply performed on mount via the Fetch API (in our tests we are utilizing the isomorphic-fetch library to polyfill Fetch for Node).

function Child() {
  useEffect(() => {
    fetch('http://localhost:8080/api/getChildList', {
      method: 'POST',
      mode: 'cors',
      cache: 'no-cache',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        pagination: {
          pageSize: 10,
          pageNo: 0,
          sortedColumn: 'createdBy',
          sortedType: 'asc',
        },
      }),
    })
      .then(async (response) => {
        console.log('Fetch complete: ', await response.json());
      })
      .catch((error) => {
        console.error('Failed to fetch: ', error);
      });
  }, []);

  return (
    <React.Fragment>
      <h1>Child</h1>
    </React.Fragment>
  );
}

Current behavior

If we are running two test suites consecutively, in this case, App.test.js and then Child.test.js in quick succession, the second server is not fully set up before the test executes, resulting in something like this:

> mocha-ref@1.0.0 test:watch
> cross-env NODE_ENV=test mocha --timeout 10000 -w src/**/*.test.js



  App tests
    ✓ renders App without crashing
Fetch complete:  {
  msgCode: 0,
  msgDesc: 'Request processed successfully',
  data: {
    data: { createdBy: 'CJ', createdDate: '02/18/2021' },
    pagination: {
      pageNo: 0,
      pageSize: 10,
      total: 1,
      sortedColumn: 'createdBy',
      sortedType: 'asc'
    }
  }
}

  Child tests
    ✓ renders Child without crashing
Failed to fetch:  FetchError: request to http://localhost:8080/api/getChildList failed, reason: connect ECONNREFUSED 127.0.0.1:8080
    at ClientRequest.<anonymous> (/home/cjones26/JS/mocha-ref/node_modules/node-fetch/lib/index.js:1461:11)
    at ClientRequest.emit (events.js:314:20)
    at Socket.socketErrorListener (_http_client.js:428:9)
    at Socket.emit (events.js:314:20)
    at emitErrorNT (internal/streams/destroy.js:92:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
    at processTicksAndRejections (internal/process/task_queues.js:84:21) {
  type: 'system',
  errno: 'ECONNREFUSED',
  code: 'ECONNREFUSED'
}


  2 passing (59ms)

ℹ [mocha] waiting for changes...

Expected behavior

> mocha-ref@1.0.0 test:watch
> cross-env NODE_ENV=test mocha --timeout 10000 -w src/**/*.test.js



  App tests
    ✓ renders App without crashing
Fetch complete:  {
  msgCode: 0,
  msgDesc: 'Request processed successfully',
  data: {
    data: { createdBy: 'CJ', createdDate: '02/18/2021' },
    pagination: {
      pageNo: 0,
      pageSize: 10,
      total: 1,
      sortedColumn: 'createdBy',
      sortedType: 'asc'
    }
  }
}

  Child tests
    ✓ renders Child without crashing
Fetch complete:  {
  msgCode: 0,
  msgDesc: 'Request processed successfully',
  data: {
    data: { createdBy: 'CJ Child', createdDate: '02/18/2021' },
    pagination: {
      pageNo: 0,
      pageSize: 10,
      total: 1,
      sortedColumn: 'createdBy',
      sortedType: 'asc'
    }
  }
}

  2 passing (59ms)

ℹ [mocha] waiting for changes...

Screenshots

Failed test: Failed test

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:16 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
msutkowskicommented, Mar 15, 2021

@cjones26 The magic here is the usage of the --file flag in the CLI (or in mocharc in this case). The server creation/cleanups are always rerun and skip the awkward handling of the lifecycle hooks behavior from mocha.

This behavior is slightly different than what I did originally where I created the server once, then set it to global.

Either works, but this is the best we can do for now. We’re going to continue to research supporting parallel mode - if we resolve that, I’ll let you know so you can update if necessary. Thanks again for providing this issue!

3reactions
msutkowskicommented, Mar 9, 2021

@kettanaito You might be right, but I’m not 100% sure and it’s kind of a pain to debug. By default, mocha runs sequentially and you have to opt-in to parallel=true. This seems to be more of a problem with how mocha is running tests, but I’m no mocha expert.

For example, in the repo as-is, if you remove the server.close() from each test file, it’ll run them all just fine but would lead to cleanup issues.

As an immediate solution that works with both sequential and parallel runs, what I’d recommend @cjones26 does is change the mocha configuration with these steps:

  1. Use root hooks - https://mochajs.org/#defining-a-root-hook-plugin (see below)
  2. Update mocha config (see below)
  3. Add hooks for the msw server (see below)
  4. Cleanup tests (see below)

In the given repro, you’d add this file:

// test/hooks.js
import { setupServer } from "msw/node";
import { rest } from "msw";

// export the server instance so you can `server.use` as needed
export const server = setupServer(
  rest.post("http://localhost:8080/api/getList", (req, res, ctx) => {
    return res(
      ctx.set("access-control-allow-origin", "*"),
      ctx.json({
        msgCode: 0,
        msgDesc: "Request processed successfully",
        data: {
          data: {
            createdBy: "CJ",
            createdDate: "02/18/2021",
          },
          pagination: {
            pageNo: req.body.pagination.pageNo,
            pageSize: req.body.pagination.pageSize,
            total: 1,
            sortedColumn: req.body.pagination.sortedColumn,
            sortedType: req.body.pagination.sortedType,
          },
        },
      })
    );
  }),
  rest.post("http://localhost:8080/api/getChildList", (req, res, ctx) => {
    return res(
      ctx.set("access-control-allow-origin", "*"),
      ctx.json({
        msgCode: 0,
        msgDesc: "Request processed successfully",
        data: {
          data: {
            createdBy: "CJ Child",
            createdDate: "02/18/2021",
          },
          pagination: {
            pageNo: req.body.pagination.pageNo,
            pageSize: req.body.pagination.pageSize,
            total: 1,
            sortedColumn: req.body.pagination.sortedColumn,
            sortedType: req.body.pagination.sortedType,
          },
        },
      })
    );
  })
);

exports.mochaHooks = {
  beforeAll: function () {
    server.listen();
  },
  afterEach: function () {
    server.resetHandlers();
  },
  afterAll: function () {
    server.close();
  },
};

Update mocha config to include the new hooks file:

// .mocharc.yaml
require:
  - "test/setupMocha.js"
  - "test/specHelper.js"
  - "test/hooks.js"

Refactor tests to look like:

import React from "react";
import { render } from "@testing-library/react";
import Child from "../Child";

describe("Child tests", () => {
  it("renders Child without crashing", () => {
    render(<Child />);
  });
});

and

import React from "react";
import { render } from "@testing-library/react";
import App from "../App";

describe("App tests", () => {
  it("renders App without crashing", async () => {
    render(<App />);
  });
});

As general guidance, I’d move the request handlers and server instance creation out into their own files so you can more easily reuse them in other places such as Storybook.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Ensuring server app runs before mocha tests start
Your server module doesn't have a callback, so it could not be ready when you call done() in your beforeEach method.
Read more >
Mocha — Global Setup and Teardown (before/after)
Creating a solid test suite for your application may require one or more setup steps before running the tests. Mocha offer two ways...
Read more >
Mocha - the fun, simple, flexible JavaScript test framework
When a test file is loaded, Mocha executes all of its suites and finds–but does not execute–any hooks and tests therein. Top-level hooks,...
Read more >
Testing Node.js with Mocha and Chai - LogRocket Blog
To complete your Mocha setup, you'll have to write a unit test for a very simple functionality and configure a script to run...
Read more >
Configuration File - WebdriverIO
The configuration file contains all necessary information to run your test suite. It's a NodeJS module that exports a JSON. Here is an...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found