{"record":{"id":"be377a526602c2f9","repo":"denoland/deno","slug":"before-requires-a-function-argument","errorCode":null,"errorMessage":"before() requires a function argument","messagePattern":"before\\(\\) requires a function argument","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/testing.ts","lineNumber":2148,"sourceCode":"  return prepareDenoTestForSuite(name, options, fn, overrides);\n}\n\nsuite.skip = function skip(name, options, fn) {\n  return suite(name, options, fn, { skip: true });\n};\nsuite.todo = function todo(name, options, fn) {\n  return suite(name, options, fn, { todo: true });\n};\nsuite.only = function only(name, options, fn) {\n  return suite(name, options, fn, { only: true });\n};\n\nconst it = test;\nconst describe = suite;\n\nfunction before(fn, _options) {\n  if (typeof fn !== \"function\") {\n    throw new TypeError(\"before() requires a function argument\");\n  }\n  if (isTapMode()) {\n    const tapSuite = getTapCurrentSuite();\n    if (tapSuite !== null) {\n      ArrayPrototypePush(tapSuite.beforeAllHooks ??= [], fn);\n      return;\n    }\n    ArrayPrototypePush(rootBeforeHooks, fn);\n    // A bare top-level `before()` with no tests must still produce TAP\n    // output (`before` runs, then `TAP version 13`, then `1..0`).\n    scheduleTapRun();\n    return;\n  }\n  if (currentSuite) {\n    ArrayPrototypePush(currentSuite.beforeAllHooks, fn);\n    return;\n  }\n  ArrayPrototypePush(rootBeforeHooks, fn);","sourceCodeStart":2130,"sourceCodeEnd":2166,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/testing.ts#L2130-L2166","documentation":"The module-level before(fn) exported by node:test (BDD-style, also active under describe/it) registers a one-time setup hook — on the current suite in TAP mode, otherwise on the root hooks. The implementation immediately checks typeof fn === 'function' and throws TypeError 'before() requires a function argument' otherwise. This is the module-level hook; the per-test-context variant is t.before (error message without 'argument').","triggerScenarios":"before() with no argument; before(setup()) passing the result of an immediate call (a value or Promise) instead of the function; before('connect', fn) using a name-first convention from other frameworks.","commonSituations":"Migrating from frameworks where hooks take a name; refactors that parenthesize the helper; wiring hooks from configuration objects where the entry can be undefined.","solutions":["Pass the function reference: before(setupDb)","Wrap expressions that must run later: before(() => init()) — but prefer passing init itself so it is awaited","Check typeof before registering config-driven hooks"],"exampleFix":"// before\nbefore(setupDatabase());\n\n// after\nbefore(setupDatabase);","handlingStrategy":"type-guard","validationCode":"import { before } from 'node:test';\n\nif (typeof setup !== 'function') {\n  throw new TypeError(`before: expected function, got ${typeof setup}`);\n}\nbefore(setup);","typeGuard":"const isHookFn = (fn) => typeof fn === 'function';\n\nif (isHookFn(setup)) {\n  before(setup);\n}","tryCatchPattern":null,"preventionTips":["Pass the function reference — before(setupDb), not before(setupDb())","No name-first argument: the first parameter is the hook itself","For async setup pass the async function so it is awaited","Validate config-driven hooks with typeof before registering"],"tags":["node-test","hooks","bdd","api-misuse"],"backgroundTag":"test-hook-requires-function","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}