Skip to content

Mocks

A common practice is to break down complex behavior into multiple cooperating classes. When unit testing a class, we don't want to test its dependencies too, so instead we introduce test doubles.

Vest provides mocks for this purpose. Mocks are objects that implement the same methods as the original class, but allow you to specify what is returned and when. In addition, mocks record their method calls, so you can validate whether a method was called and with what parameters.

extends VestTest

func get_suite_name() -> String:
  return "Mocks"

var simple_math: SimpleMath

func suite():
  on_case_begin.connect(func(__):
    simple_math = mock(SimpleMath)

    # Specify default answers
    when(simple_math.times).then_return(0)
    when(simple_math.sum).then_return(0)
  )

  test("Return configured answer", func():
    when(simple_math.times).with_args([2.0, 2.0]).then_return(5)
    expect_equal(simple_math.times(2, 2), 5)
  )

  test("Method was called at least once", func():
    simple_math.times(4, 4)

    var calls := get_calls_of(simple_math.times)
    expect_not_empty(calls, "Method was not called!")
  )

  test("Method was called with parameters", func():
    simple_math.times(2, 4)
    expect_contains(get_calls_of(simple_math.times), [2.0, 4.0])
  )
extends VestTest

func get_suite_name() -> String:
  return "Mocks"

var simple_math: SimpleMath

func before_case(__):
  simple_math = mock(SimpleMath)

  # Specify default answers
  when(simple_math.times).then_return(0)
  when(simple_math.sum).then_return(0)

func test_return_configured_answer():
  when(simple_math.times).with_args([2.0, 2.0]).then_return(5)
  expect_equal(simple_math.times(2, 2), 5)

func test_method_was_called_at_least_once():
  simple_math.times(4, 4)

  var calls := get_calls_of(simple_math.times)
  expect_not_empty(calls, "Method was not called!")

func test_method_was_called_with_parameters():
  simple_math.times(2, 4)
  expect_contains(get_calls_of(simple_math.times), [2.0, 4.0])
extends RefCounted
class_name SimpleMath

func times(a: float, b: float) -> float:
  return a * b

func sum(a, b):
  return a + b

Creating mocks

Call mock() with the desired class, and it will return a mocked instance.

Under the hood, vest creates a new class that extends the class being mocked. This mock class will record all function calls, and delegate them to the defined answers.

Limitations

Inner classes are not supported at the moment.

Built-in classes are not supported at the moment.

Default values in methods might not work, requiring calls where each parameter is present.

Since property setters and getters can't be overridden in extending classes, they are not supported.

Tip

Many Godot classes implement get_property() and set_property() methods and then use those as getters and setters for property. Using this pattern, vest can mock the getter and setter methods, which would be then used by the property.

Defining answers

Unless an answer is defined for the call, mocked methods won't return anything. Answers may be defined for any call to the method, or for calls with specific parameter values. Answers with parameter values take precedence over generic answers.

Use when() to start defining an answer for a given method call, passing in the method itself as parameter. Optionally call with_args() on the result, to specify expected parameter values.

Warning

Make sure that the expected parameter values are the right type! In the example, we intentionally pass in [2.0, 2.0] as expected parameters - even though [2, 2] is more natural to write, they are ints, and the method is called with floats, so the answer won't match any calls.

.then_return()
Returns the value passed in as parameter.
.then_answer()
Takes a callable as parameter. Whenever the answer is used, it will call the specified callable and return its return value. The callable receives an array, containing the parameters used to call the mocked method.

Asserting calls

Use get_calls_of() to retrieve all the calls of a mocked method. Similarly to captured signals, the result is an array of arrays, where each individual array contains the arguments used to call the mocked method.

Since these are just arrays, the assert library can be used to verify requirements.