We recommend familiarizing with [Decision Table Testing](/codex/decision-table-testing) before reading this article.
While Decision Table testing focuses on multiple input conditions producing one output, Cause-Effect Graph Testing looks at how a set of system conditions (causes) affect a set of system outcomes (effects). Each cause and effect should be a boolean variable and there should be a clear boolean relationship between them – each effect should be defined as a boolean function over the causes. The resulting diagram is called the Cause-Efect Graph and each combination of inputs indicates a possible test case.
Cause-Effect Graph Testing can be used to test functions with several side effects such as multiple state changes and/or event emissions during one function.
### Worked example
Let's look at the OpenZeppelin ERC20 `transferFrom` implementation.
-- CODE language-solidity --
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
The causes (input conditions) for `transferFrom` are the following:
- A: Valid sender: `sender != 0x0`
- B: Valid recipient: `recipient != 0x0`
- C: Sufficient sender funds: `balanceOf(sender)` >= `amount`
- D: Sufficient sender allowance: `allowances(sender, recipient)` >= `amount`
To complete the Cause-Effect Graph, we need to define the possible effects and express them in terms of the causes.
The possible effects are the following:
- Return (A & B & C & D)
- Update balances (A & B & C & D)
- Emit `Transfer` event (A & B & C & D)
- Revert:
- Revert invalid sender if !A
- Revert invalid recipient if A & !B
- Revert insufficient allowance if A & B & !D
- Revert insufficient funds if A & B & D & !C.