Defining Custom Assertions - dicksonlaw583/gmassert2 GitHub Wiki

Defining Custom Assertions

Sometimes it is useful to define your own assertions to suit specific testing needs. To do this:

  • Define a kernel function that takes 2 arguments (got and expected), and returns a Boolean value.
  • (Optional) Define a debug-value function that turns received and/or expected values into a human-readable string.
  • Create a developer-facing assertion function that calls assert_operation().

assert_operation()

assert_operation() takes up to 7 arguments, which can be adjusted to suit the display needs of your custom assertion:

  • got: The value received by the assertion.
  • expected: The reference value to check the got value against. Can be stubbed out with undefined if not used.
  • op: The ID of a kernel function that takes got and expected as arguments.
  • invert: true for failing when op(got, expected) returns false, false for failing when op(got, expected) returns true.
  • msg: (Optional) The message to display when the assertion fails. Default: "Assertion Failed!"
  • debug_got: (Optional) Can be undefined or __gma_debug_value__ to display the got value in debug format, or a function that takes the got value and returns a human-readable string. Default: __gma_debug_value__
  • debug_expected: (Optional) Can be undefined or __gma_debug_value__ to display the expected value in debug format, a string explaining what is expected, or a function that takes the expected value and returns a human-readable string. Default: __gma_debug_value__

__gma_debug_value__()

Sometimes it is useful to revert to the default debug format for received and/or expected values that are completely unexpected (e.g. undefined when a data structure ID is expected). For that, you can call the hidden function __gma_debug_value__() in the debug-value function(s).

Example with only a received value

Objective: You wish to create an assertion to check if a map is empty.

Start with the kernel function __assert_map_empty__:

///@func __assert_map_empty__(map)
///@arg {Id.DsMap} map The ID of the map to check
///@return {Bool}
///@ignore
function __assert_map_empty__(map) {
  return ds_exists(map, ds_type_map) && ds_map_empty(map);
}

Since map IDs are virtually useless on their own, you can create a debug-value function that renders the map in JSON form. You should also handle cases where a completely unwanted value is received or the map ID does not exist:

///@func __debug_map_value__(val)
///@arg {Any} val The value to derive a debug value from
///@return {String}
///@ignore
function __debug_map_value__(val) {
  // Can't possibly be a map ID?
  if (typeof(val) != "number") {
    return __gma_debug_value__(val);
  }
  // Map doesn't exist?
  if (!ds_exists(val, ds_type_map)) {
    return "(Map " + string(val) + ")\nDOES NOT EXIST";
  }
  // Show in JSON form
  return "(Map " + string(val) + ")\n" + json_encode(val);
}

Now finish with the main developer-facing assertion function:

///@function assert_map_empty(got, [msg])
///@param {Id.DsMap} got The received value (should be a map ID).
///@param {String} [msg] (optional) The message to display if the assertion fails.
///@desc Assert that the given map is empty.
function assert_map_empty(got, msg="Map doesn't exist or isn't empty!") {
  // Run assert_operation (debug_got overridden, debug_expected is a fixed message)
  assert_operation(got, undefined, __assert_map_empty__, false, msg, __debug_map_value__, "An empty map");
}

Example with a received and expected value

Objective: You wish to create an assertion to check if a priority queue contains a given value.

Start with the kernel function __assert_pq_exists__:

///@func __assert_pq_exists__(pq, val)
///@param {Id.DsPriority} pq The priority queue to check
///@param {Any} val The value to look for
///@return {Bool}
function __assert_pq_exists__(pq, val) {
  return ds_exists(pq, ds_type_priority) && ds_priority_find_priority(pq, val);
}

Since priority queue IDs are virtually useless on their own, you can create a debug-value function that describes its size, minimum, and maximum values. You should also handle cases where a completely unwanted value is received or the priority queue ID does not exist:

///@func __debug_pq_value__(val)
///@param {Any} val 
///@return {String}
///@ignore
function __debug_pq_value__(val) {
  // Can't possibly be a priority queue ID?
  if (typeof(val) != "number") {
    return __gma_debug_value__(val);
  }
  // Priority queue doesn't exist?
  if (!ds_exists(val, ds_type_priority)) {
    return "(Priority Queue " + string(val) + ")\nDOES NOT EXIST";
  }
  // Debug size, lowest, highest
  var result = "(Priority Queue " + string(val) + ")\nSize: " + string(ds_priority_size(val));
  if (!ds_priority_empty(val)) {
    result += "\nMin: " + __gma_debug_value__(ds_priority_find_min(val), true)
      + "\nMax: " + __gma_debug_value__(ds_priority_find_max(val), true);
  }
  return result;
}

Let's add a quick description before the expected value's debug value as well:

///@func __debug_pq_exists_expected_value__(val)
///@param {Any} val
///@return {String}
///@ignore
function __debug_pq_exists_expected_value__(val) {
  return "A priority queue that contains: " + __gma_debug_value__(val);
}

Now finish with the main developer-facing assertion function:

///@func assert_pq_exists(got, val, msg)
///@param {Id.DsPriority} got The actual received value for the assertion
///@param {Any} val The value to look for
///@param {String} msg (optional) A custom message to display when the assertion fails
///@desc Assert that the given priority queue contains a given value.
function assert_pq_exists(got, val, msg="Priority queue is inexistent or doesn't contain the value!") {
  // Run assert_operation (expected value provided, debug_got and debug_expected overridden)
  assert_operation(got, val, __assert_pq_exists__, false, msg, __debug_pq_value__, __debug_pq_exists_expected_value__);
}