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

Make a passing .toThrow() not dump to console.error #5785

Closed
nemoDreamer opened this issue Mar 12, 2018 · 10 comments
Closed

Make a passing .toThrow() not dump to console.error #5785

nemoDreamer opened this issue Mar 12, 2018 · 10 comments

Comments

@nemoDreamer
Copy link

Do you want to request a feature or report a bug?

Feature

What is the current behavior?

Writing a test for an exception still dumps that exception to the output, which is very confusing:
It took me a while to realize that buried after screen and screens of red stack traces, my tests were showing as PASS...

I don't want to use --silent during development, as I'd use the ability to quickly console.log the shape of objects I'm trying to expect, etc...

As a work-around, I'm creating a spy on console.error, but that of course also swallows and exceptions that I'm not yet testing for:

beforeAll(() => {
  jest.spyOn(console, 'error').mockImplementation(() => undefined);
});

afterAll(() => {
  console.error.mockRestore();
});

What is the expected behavior?

It would be great if exceptions happening inside .toThrow would not get dumped to console IF they PASS the assertion. (I guess that would take some kind of buffering of the output?)

Please provide your exact Jest configuration

const path = require('path');

const isCI = process.argv.includes('--ci');

module.exports = {
  rootDir: '../packages',
  testMatch: ['**/__tests__/*.{js,jsx}', '**/*.{spec,test}?(s).{js,jsx}'],

  collectCoverage: isCI,
  collectCoverageFrom: [
    '**/src/**?(/)*.{js,jsx}',
    // ignore:
    '!**/__stories__/**',
    '!**/*.stories.{js,jsx}',
  ],
  coverageDirectory: path.resolve(__dirname, '..', 'coverage'),
  coverageThreshold: isCI
    ? {
        global: {
          branches: 100,
          functions: 100,
          lines: 100,
          statements: 0,
        },
      }
    : undefined,
};

Run npx envinfo --preset jest in your project directory and paste the
results here

  System:
    OS: macOS High Sierra 10.13.3
    CPU: x64 Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
  Binaries:
    Node: 8.8.1
    Yarn: 1.5.1
    npm: 5.4.2
  npmPackages:
    jest:
      wanted: ^22.4.2
      installed: 22.4.2
@SimenB
Copy link
Member

SimenB commented Mar 18, 2018

Can you provide a reproduction repo?

@nemoDreamer
Copy link
Author

You're right... As I was isolating this into a repro repo, everything behaved as expected with a call to vanilla jest...:

index.test.js:

const caughtFunction = () => {
    try {
        blarg;
    } catch (e) {
        throw new Error('Ghaaa!');
    }
}

const throwingFunction = () => {
    throw new Error('Ghaaa!');
}

const badFunction = () => {
    blarg;
}

describe('toThrow', function() {
    // When asserting .toThrow, the error is expected.
    // So this should not pollute the terminal output with a stacktrace:
    it('should not vomit to console', () => {
        // This is nicely silent:
        expect(() => {
            caughtFunction();
        }).toThrow('Ghaaa!')

        // and this too:
        expect(() => {
            throwingFunction();
        }).toThrow('Ghaaa!')

        // and even this...:
        expect(() => {
            badFunction();
        }).toThrow()
    })
});

My function that is dumping to console is very similar to the caughtFunction example above. Jest a try/catch, except the catch is being dumped as a full red error message + stacktrace in the middle of the test run.

I compared my --showConfig to vanilla, and found no differences that would impact this (dir / regex changes), other than a custom resolver:

resolver: path.resolve(__dirname, 'resolver'), so ".jest/resolver.js":

/*
|-------------------------------------------------------------------------------
| Custom 'module resolver' for Jest
|-------------------------------------------------------------------------------
|
| Forked from Jest, this is the default 'resolver' with the added benefit of
| remapping the "main" field to the value of the "module" field, when present.
|
*/

const fs = require('fs');
const path = require('path');
const resolve = require('resolve');

function mapModuleFieldToMain(pkg, pkgDir) {
  if (/^@sgds\//.test(pkg.name)) {
    const moduleSrcPath = pkg.module;
    const isModuleFieldAvailable =
      moduleSrcPath && fs.existsSync(path.resolve(pkgDir, moduleSrcPath));

    if (isModuleFieldAvailable) {
      return Object.assign({}, pkg, { main: moduleSrcPath });
    }
  }

  return pkg;
}

function defaultResolver(pkgPath, options) {
  return resolve.sync(pkgPath, {
    basedir: options.basedir,
    extensions: options.extensions,
    moduleDirectory: options.moduleDirectory,
    paths: options.paths,
    packageFilter: mapModuleFieldToMain,
  });
}

module.exports = defaultResolver;

My full config:

jest v22.4.2

{
  "configs": [
    {
      "automock": false,
      "browser": false,
      "cache": true,
      "cacheDirectory": "/var/folders/bx/2d_zp_nd1jvbqx3wxlbx8ynh0000gp/T/jest_dy",
      "clearMocks": false,
      "coveragePathIgnorePatterns": [
        "/node_modules/"
      ],
      "detectLeaks": false,
      "forceCoverageMatch": [],
      "globals": {},
      "haste": {
        "providesModuleNodeModules": []
      },
      "moduleDirectories": [
        "node_modules"
      ],
      "moduleFileExtensions": [
        "js",
        "json",
        "jsx",
        "node"
      ],
      "moduleNameMapper": {},
      "modulePathIgnorePatterns": [],
      "name": "dda8693683a0e13b65045be219864bb1",
      "resetMocks": false,
      "resetModules": false,
      "resolver": "/Users/philip/Development/Autodesk/shotgun-ux/sgds/.jest/resolver.js",
      "restoreMocks": false,
      "rootDir": "/Users/philip/Development/Autodesk/shotgun-ux/sgds",
      "roots": [
        "/Users/philip/Development/Autodesk/shotgun-ux/sgds"
      ],
      "runner": "jest-runner",
      "setupFiles": [
        "/Users/philip/Development/Autodesk/shotgun-ux/sgds/node_modules/regenerator-runtime/runtime.js"
      ],
      "snapshotSerializers": [],
      "testEnvironment": "jest-environment-jsdom",
      "testEnvironmentOptions": {},
      "testLocationInResults": false,
      "testMatch": [
        "**/__tests__/**/*.js?(x)",
        "**/?(*.)(spec|test).js?(x)"
      ],
      "testPathIgnorePatterns": [
        "/node_modules/"
      ],
      "testRegex": "",
      "testRunner": "/Users/philip/Development/Autodesk/shotgun-ux/sgds/node_modules/jest-jasmine2/build/index.js",
      "testURL": "about:blank",
      "timers": "real",
      "transform": [
        [
          "^.+\\.jsx?$",
          "/Users/philip/Development/Autodesk/shotgun-ux/sgds/node_modules/babel-jest/build/index.js"
        ]
      ],
      "transformIgnorePatterns": [
        "node_modules/(?!@sgds)"
      ],
      "watchPathIgnorePatterns": []
    }
  ],
  "globalConfig": {
    "bail": false,
    "changedFilesWithAncestor": false,
    "collectCoverage": false,
    "collectCoverageFrom": [
      "**/src/**?(/)*.{js,jsx}",
      "!**/__stories__/**",
      "!**/*.stories.{js,jsx}"
    ],
    "coverageDirectory": "/Users/philip/Development/Autodesk/shotgun-ux/sgds/coverage",
    "coverageReporters": [
      "json",
      "text",
      "lcov",
      "clover"
    ],
    "detectLeaks": false,
    "expand": false,
    "globalSetup": null,
    "globalTeardown": null,
    "listTests": false,
    "maxWorkers": 7,
    "noStackTrace": false,
    "nonFlagArgs": [],
    "notify": false,
    "notifyMode": "always",
    "passWithNoTests": false,
    "rootDir": "/Users/philip/Development/Autodesk/shotgun-ux/sgds",
    "runTestsByPath": false,
    "testFailureExitCode": 1,
    "testPathPattern": "",
    "testResultsProcessor": null,
    "updateSnapshot": "new",
    "useStderr": false,
    "verbose": null,
    "watch": false,
    "watchman": true
  },
  "version": "22.4.2"
}

@SimenB
Copy link
Member

SimenB commented Mar 19, 2018

Only guess I have is async vs sync, or some custom unhandled error/rejection handler.

Without a reproduction there's not much we can do here, though. Happy to reopen if you're able to create one 🙂

@SimenB SimenB closed this as completed Mar 19, 2018
@nemoDreamer
Copy link
Author

Yeah, can't give you access to our private repo, sorry 😜
Thanks for looking into it!

Will try to isolate async/sync.

My workaround works for now, and when I get round to figuring it out, I'll drop the answer in here for you and @antoinerey

@kevinbarabash
Copy link

I'm experiencing this when using enzyme's mount() to render a component that throws in its render() function. I'm guess it has something to do with the way that jsdom's error handling works.

@josemigallas
Copy link

Same happening here, tests are passing but the console turns red every time I run them.

@kevinbarabash
Copy link

A solution to this was posted in #5267 (comment).

@davidgilbertson
Copy link

I'm doing this, thanks @kevinbarabash for the link.

export const expectToThrow = func => {
  // Even though the error is caught, it still gets printed to the console
  // so we mock that out to avoid the wall of red text.
  jest.spyOn(console, 'error');
  console.error.mockImplementation(() => {});

  expect(func).toThrow();

  console.error.mockRestore();
};

@Kashuab
Copy link

Kashuab commented Jan 28, 2021

Thanks @kevinbarabash. If anyone is looking for a typed version (with support for assertions against the type of error,) I'm currently using this:

/**
 * Helps prevent error logs blowing up as a result of expecting an error to be thrown,
 * when using a library (such as enzyme)
 *
 * @param func Function that you would normally pass to `expect(func).toThrow()`
 */
export const expectToThrow = (func: () => unknown, error?: JestToErrorArg): void => {
  // Even though the error is caught, it still gets printed to the console
  // so we mock that out to avoid the wall of red text.
  const spy = jest.spyOn(console, 'error');
  spy.mockImplementation(() => {});

  expect(func).toThrow(error);

  spy.mockRestore();
};

type JestToErrorArg = Parameters<jest.Matchers<unknown, () => unknown>['toThrow']>[0];

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 10, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants