Skip to content

beforeEach hook issue #3356

@thoughtsunificator

Description

@thoughtsunificator

avajs beforeEach hook issue

I have test cases where a Foo object that has a bar method which tracks its calls.
Within the beforeEach hook, a Foo object is constructed and its bar method is called with the argument foo, where foo is equal to the same Foo object.

foo.js
/**
 * A Foo object that has a bar method which has its calls tracked in a "mock" object property.
 */
function Foo() {
	const mock = { calls: [] }
	Object.getPrototypeOf(this.bar).mock = mock
	this.bar = function() {
		mock.calls.push({ arguments })
	}
}
Foo.prototype.bar = function() {}

export default Foo

What's wrong

Running tests in parallel causes the test.context.bar.prototype to not be strictly equal to foo.bar.mock.calls[0].arguments[0].foo.bar.prototype.

  • There is no difference between the three cases they're just here to demonstrate running test cases in parallel
  • No issue occurs when running tests in serial mode or when there is only one case.
  • The last test case will always succeed.

Tested on versions:

  • 6.2.0

Inside the following env:

  • Linux
  • Node v23.1.0
  • npm v10.9.0

Actual

call-from-within-case.ava.test.js (KO)
import ava from "ava"
import Foo from "./foo.js"

ava.beforeEach((test) => {
	test.context.foo = new Foo()
})

ava("Call from within test case: Case 1", (test) => {
	const { foo } = test.context
	test.context.foo.bar({ foo: test.context.foo })
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

ava("Call from within test case: Case 2", (test) => {
	const { foo } = test.context
	test.context.foo.bar({ foo: test.context.foo })
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

ava("Call from within test case: Case 3", (test) => {
	const { foo } = test.context
	test.context.foo.bar({ foo: test.context.foo })
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})
call-from-within-hook.ava.test.js (KO)
import ava from "ava"
import Foo from "./foo.js"

ava.beforeEach((test) => {
	test.context.foo = new Foo()
	test.context.foo.bar({ foo: test.context.foo })
})

ava("Call from within hook: Case 1", (test) => {
	const { foo } = test.context
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

ava("Call from within hook: Case 2", (test) => {
	const { foo } = test.context
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

ava("Call from within hook: Case 3", (test) => {
	const { foo } = test.context
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})
no-hook.ava.test.js (OK)
import ava from "ava"
import Foo from "./foo.js"

ava("No hook: Case 1", (test) => {
	const foo = new Foo()
	foo.bar({ foo })
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

ava("No hook: Case 2", (test) => {
	const foo = new Foo()
	foo.bar({ foo })
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

ava("No hook: Case 3", (test) => {
	const foo = new Foo()
	foo.bar({ foo })
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})
serial.ava.test.js (OK)
import ava from "ava"
import Foo from "./foo.js"

ava.serial.beforeEach((test) => {
	test.context.foo = new Foo()
	test.context.foo.bar({ foo: test.context.foo })
})

ava.serial("Serial: Case 1", (test) => {
	const { foo } = test.context
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

ava.serial("Serial: Case 2", (test) => {
	const { foo } = test.context
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

ava.serial("Serial: Case 3", (test) => {
	const { foo } = test.context
	test.is(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})
call-from-within-hook.node.test.js (OK)
import nodeTest from 'node:test';
import assert from 'node:assert/strict';
import Foo from "./foo.js"

nodeTest.beforeEach((test) => {
	test.foo = new Foo()
	test.foo.bar({ foo: test.foo })
})

nodeTest("Call from within hook (Node): Case 1", (test) => {
	const { foo } = test
	assert.strictEqual(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

nodeTest("Call from within hook (Node): Case 2", (test) => {
	const { foo } = test
	assert.strictEqual(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

nodeTest("Call from within hook (Node): Case 3", (test) => {
	const { foo } = test
	assert.strictEqual(
		foo.bar.mock.calls[0].arguments[0].foo.bar.prototype,
		foo.bar.prototype,
		"Case 1 foo.bar.prototype strict equality failed"
	)
})

You can find these test cases here.

Expected

It is expected that all tests pass.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions