14

Is there a way to trigger npm's posttest when test fails? If package.json contains

"scripts": {
  "pretest": "echo pretest",
  "test": "some_failed_test_or_error",
  "posttest": "echo posttest"
}

$ npm test will echo "pretest" but not "posttest".

I get the same behavior if I use mocha and a test fails, even if mocha doesn't trigger any exceptions (just some simple failure assert(true==false)).

I'm launching a resource on pretest, and I'd like to kill the resource on posttest, whether the test itself passes or fails.

MacOS OS X 10.9.4, npm version 1.4.21, node v0.10.30.

jimm101
  • 948
  • 1
  • 14
  • 36

3 Answers3

19

Worked it out. Not sure if the following will work in Windows. The bash || operator followed by an empty comment : changes the exit code.

For instance, using mocha:

"scripts": {
  "pretest": "echo pretest",
  "test": "mocha || :",
  "posttest": "echo posttest"
}
jimm101
  • 948
  • 1
  • 14
  • 36
  • 4
    This helped me out, but it doesn't work on Windows. I used `mocha || true` to get this working cross-platform. – woodedlawn Aug 17 '15 at 19:06
  • 4
    But how would you keep the build status? When mocha fails, you would probably like to see the build failed. `mocha || true` hides the real exit code, you will not see when tests failed. – just-boris May 10 '18 at 17:09
  • Yes ! This simple solution works, and I get to see the err as well – mwarren Aug 29 '18 at 09:45
  • 9
    I think this might not work for CI builds. I believe they rely on exit codes to tell if the tests are failing. That might be what just-boris was referring to as well. – I. Cantrell Apr 26 '19 at 21:59
10

The other answer to this question is neat and short, but is a little unorthodox. I know that if I used it a big questionmark would form over my head every time I came back and saw " || : ". It's so concise you might even miss it. It would need documentation somewhere.

I spent a little time looking for a more orthodox solution and found it here: https://github.com/npm/npm/issues/5493

You need npm-run-all

"test": "npm-run-all the-actual-test run-after-test-even-if-failed --continue-on-error"

However you cannot use "pretest" or "posttest" names for the pre and post scripts, because they would run twice.

mwarren
  • 2,409
  • 1
  • 22
  • 28
1

I have what I think is a better solution here: I added an exceedingly simple bash script called nofail.sh to the root of my project, which is nothing but the following:

"$@" || true

Then, I added ./nofail.sh at the beginning of my npm test command like so:

"scripts": {
  "pretest": "echo pretest",
  "test": "./nofail.sh mocha",
  "posttest": "echo posttest"
}

If you don't like adding a bash script to the mix, or need something cross-platform, suppress-exit-code is a node package that seems to fit the bill, just install that and use suppress-exit-code instead of ./nofail.sh.

But why?

For one, I think it's clearer about what is happening - we are ignoring the failure code from the test command. But more importantly, this allows you to pass arguments to your test command as normal, so that this still works:

npm test -- test/to/run.js

Both other solutions break this case: in the || : the arguments are passed to the : command† instead, where they do nothing, and in the all-in-one they get passed to npm-run-all.

This was particularly troublesome in our project, because our base test script has a couple configuration parameters, and then we have several "suites" defined as individual scripts that specified a given set of test files as arguments. Because we weren't able to run npm test -- --spec "tests/some-suite/*.spec.js", we had to repeat the configuration and setup for the base test runner in each suite, in addition to the strange-looking || : at the end of each script, which made things much more verbose and messier than necessary.

With this solution, the base test configuration can be given once in npm test, and then used by all the suites, and everything is much clearer and easier to maintain.

But how?

$@ is a special variable that is set to whatever arguments are passed into the script. If the npm scripts were actual bash scripts, we could simply run mocha "$@" || true and be done with it, but they are not, so they don't have access to that variable.

So instead, we use this very simple bash script and pass our original command as arguments to it, which includes any arguments passed with --. It then runs those arguments as a command and ignores the result, which is what we want. Quoting $@ here ensures that any quoted arguments are handled correctly.


† While it is sometimes used similarly, : is not actually a "comment" in any meaningful sense, it is a builtin command like any other and is documented as such in Bash's manpage:

: [arguments]

No effect; the command does nothing beyond expanding arguments and performing any specified redirections. A zero exit code is returned.

These days : is just a confusing holdover from the early days when true was not widely available. I recommend using true these days, which fulfills the same function and makes it somewhat clearer what is going on.

cincodenada
  • 2,877
  • 25
  • 35