A mock interpreter replaces live effects with controlled results, letting isolated tests exercise business logic without invoking native or external functions. A dedicated interpreter keeps testing behavior separate from the live interpreter; configurable mocks can also preserve selected real effects. section 15.2.2 section 15.2.3
A practical mock interpreter typically supports:
- Descending through hierarchical actions when a test needs to mock lower-level calls rather than only the outer operation. section 15.2.3
Driving a random activity scenario
A random activity selector first chooses an activity and may then choose a game. Supplying [3, 2] routes execution through the computer-game branch and selects “Dwarf Fortress,” making a nondeterministic path testable.
Listing 15.15 Updated white-box test that uses a real runtime
spec :: Spec
spec
= around withAppRuntime
$ describe "Labyrinth KV DB tests"
$ it "Load player position should throw exception"
$ \appRt -> do
mocksVar <- newIORef
[ (0, RunRealInterpreter) #1
, (1, Mock (MockedKVDBConn "LabKVDB")) #2
, (2, Mock (Right testKVDBLabyrinth))
, (3, RunRealInterpreter) #3
, (4, Mock (MockedKVDBConn "LabKVDB")) #4
, (5, Mock (Left (DBError "KVDB Failure")))
, (6, RunRealInterpreter) #5
]
let testRt = TestRuntime (Just appRt) mocksVar
let action = loadGame testKVDBConfig 1
eGameState <- try (Test.runAppL testRt action)
case eGameState of
Left (e :: SomeException) ->
show e `shouldBe` "Failed to load the player pos"
Right _ -> fail "Test failed"
Inspect a white-box test that mixes mocked calls with RunRealInterpreter entries, showing how a test runtime can combine simulated and live evaluation. section 15.2.3
