liveBook · Manning

Value-based test

As explained in The Art of Unit Testing 3e

A test that verifies behavior by examining the value returned by the code under test.

A value-based test checks what code gives back. Call a function through its entry point, capture its return value, and assert that value. Return values are direct outputs, so this is usually the simplest kind of exit point to test: the test observes behavior from the outside rather than how the behavior was produced. section 1.5 section 8.3.1

The key question is not “Which internal function ran?” but “What visible result did the caller receive?” Testing a protected helper call couples a test to an implementation detail. Renaming, removing, or reorganizing that helper can break the test even when the observable behavior remains correct. When a public function returns the needed information, assert that result instead. section 8.3.1 section 5.7

Choose the exit point that matches the behavior you need to prove:

  • Use a value-based test when calling the unit returns a value that reveals success, failure, or computed output. section 1.5 section 8.3.1
  • Use a state-based test when a call changes a visible sibling property or function result instead of returning the relevant information. section 1.5 section 8.3.1
  • Reserve interaction tests with mocks for third-party dependencies when no return value or visible state can prove the behavior. section 5.6.1 section 5.7

Test the verifier’s result, not its helper call

A password verifier can call an internal findFailedRules helper and then return true or false from verify(input). A durable test calls verify("abc") and asserts the boolean result. It does not replace findFailedRules with a mock or assert that the helper was called, because the helper is not the externally meaningful exit point. section 8.3.1

Listing 6.4 Integration test with async/await
test("NETWORK REQUIRED2 (await): correct content, true", async () => {
  const result = await samples.isWebsiteAliveWithAsyncAwait();
  expect(result.success).toBe(true);
  expect(result.status).toBe("ok");
});

Async/await can keep an asynchronous result test in the familiar value-based shape: await the returned result, then assert its visible fields. section 6.1.3

Value-based tests are not limited to synchronous functions. With async/await, await the operation and inspect the resolved result using the same Arrange-Act-Assert flow. The underlying operation can still be asynchronous enough to count as an integration test, but the test is clearer than callback-based assertions. section 6.1.3

How The Art of Unit Testing 3e covers it

How deep it goes
Explains exit-point selection and maintainability consequences, while deferring detailed mock refactoring techniques.
Examples
Uses PasswordVerifier4’s verify result and an async website-status result asserted with async/await.
What you can do after
Evaluate/understand whether behavior is better proven through a return value than through mocked internal calls.
How it connects
Connects return values with state-based tests, mock objects, interaction testing, and asynchronous integration testing.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Art of Unit Testing 3e. The section links point to the parts of the book it draws on.