Testing with Mocha and opening URL's

Testing with Mocha and opening URL's header image

After my previous blog on Mocha and testing with local files I realized that I had another experience with writing test cases. This time instead of local files it was about auto opening URL’s. As you can imagine opening a URL in a browser is not something you want to see happening when running tests. So this blog focusses on writing a test case that you can use to mock the opening of a URL. We still want to retain a 100% code coverage and make sure our code runs stable. However we do not want to actually open a browser when executing our tests. We do not know what browsers are available and rely on open. It allows you to open a URl in the user’s preferred application.

How to test something with a dependency on the browser

The open library is straight forward in that you can call it using the URL and thats all you need to know. open(https://contoso.com) will open the URL for you. However using that approach you cannot write test cases for the scenario. Open is being handled as a module and thus requires a slightly different approach. To make it testable we need to write a custom function. In the sample we have the openBrowser that is called async using the .this.openBrowser().then to make sure we can log and close our CLI content.

private async openBrowser(issueLink: string): Promise<ChildProcess> {
  return this.open(issueLink, { wait: false });
}

this.openBrowser(issueLink).then((): void => {
  logger.log(issueLink);
  cb();
});

Now that the initial code is working; links can be opened in the browser, the next step is to focus on the tests. The goal is to check if openBrowser is called and essentially override it. This can be achieved using a test spy. We also want to make sure that our open module is not being called and override that. Since we are executing multiple tests we also need to clear out results from the previous tests.

Translating the our objectives into pseudo code:

  1. create a test spy to capture results;
  2. hook our test spy to our openBrowser function;
  3. override the default open behavior;
  4. validate the results of our test spy;
  5. reset any results from previous tests;

And that pseudo code results in the following code snippet:

let openBrowserSpy: Sinon.SinonSpy;

before(() => {
  (command as any).open = () => { };
  openBrowserSpy = sinon.spy(command as any, 'openBrowser');
});

afterEach(() => {
  openBrowserSpy.resetHistory();
});

it('Opens URL for a command (debug)', (done) => {
  command.action(logger, {
    options: {
      debug: true,
      type: 'command'
    }
  } as any, () => {
    try {
      openBrowserSpy.calledWith("https://aka.ms/cli-m365/new-command");
      done();
    }
    catch (e) {
      done(e);
    }
  });
});

You can find the some more details in the initial CLI for Microsoft 365 commit. Again I learned a lot on how to write test cases and test specific functions.

Loading comments…