liveBook · Manning

Return value

As explained in The Art of Unit Testing 3e

A return value is data produced by a function and delivered to its caller when the function completes.

A return value is data a function gives back to its caller when it finishes. In testing, it is a publicly visible exit point: a result the caller can inspect without reaching into private implementation details. A useful return value is one that is not `undefined`. section 1.4 section 1.3

A unit of work can have several exit points, including a return value, a state change, or a call to a third party. A unit test invokes the work through an entry point and checks one specific exit point. When a return value is available, a value-based test can usually assert it directly. section 1.4 section 1.3

Returning the result of a calculation

A `sum` function splits two comma-separated numbers, converts them, adds them, and returns the result. A test can call `sum("1,2")` and check the returned value, rather than inspecting how the calculation was performed internally. section 1.3

Prefer checking a return value over verifying calls to protected or internal functions. Testing an internal call couples a test to implementation details, so harmless refactoring can force test changes. If `verify()` returns a boolean, assert that boolean; do not mock and verify its internal `findFailedRules()` call. section 8.3.1

Listing 8.11 Production code that calls a protected function
export class PasswordVerifier4 {
  private _rules: ((input: string) => boolean)[];
  private _logger: IComplicatedLogger;
 
  constructor(rules: ((input) => boolean)[],
      logger: IComplicatedLogger) {
    this._rules = rules;
    this._logger = logger;
  }
 
  verify(input: string): boolean {
    const failed = this.findFailedRules(input);   #1
 
    if (failed.length === 0) {
      this._logger.info("PASSED");
      return true;
    }
    this._logger.info("FAIL");
    return false;
  }
 
  protected findFailedRules(input: string) {      #2
    const failed = this._rules
      .map((rule) => rule(input))
      .filter((result) => result === false);
    return failed;
  }
}
#1 Call to the internal function
#2 Internal function

The `verify` method illustrates a clear return-value exit point: it returns `true` when no rules fail and `false` otherwise, while its helper remains internal. section 8.3.1

How The Art of Unit Testing 3e covers it

How deep it goes
Defines return values as observable exit points and contrasts them with state changes and third-party calls, treating them as testing boundaries.
Examples
Uses a number-summing function and a password verifier that returns booleans while calling an internal helper.
What you can do after
Evaluate/understand unit-test targets by asserting a function’s return value instead of its internal calls when possible.
How it connects
Connects return values with unit of work, entry points, exit points, value-based tests, state-based tests, third-party tests, and mocks.

Also explained in

Compare how 8 books explain return value →

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.