Getting Started with the Mock Framework

Use of the Mock Framework

The mock framework itself is part of the unit tests in the Cangjie standard library. Before using the mock framework, you need to import unittest.mock.* and unittest.mock.mockmacro.* to the test file.

To use the CJPM tool, you only need to run the cjpm test command. Then, the mock framework is started automatically.

For direct use of CJC, see Compilation Using CJC.

Example

Commonly used mock test cases are as follows:

  • calling the mock constructor to create a mock/spy object;
  • calling the configuration API to set the mock behavior;
  • replacing test code dependencies with mock objects; and
  • (optional) calling the verification API to verify the interaction between the test code and a mock/spy object.

The following simple API is used as an example:

public interface Repository {
    func requestData(id: UInt64, timeoutMs: Int): String
}

public class Controller {
    public Controller(
        private let repo: Repository
    ) {}

    public func findData(id: UInt64): ?String {
        try {
            return repo.requestData(id, 100)
        }
        catch (e: TimeoutException) {
            return None
        }
    }
}

public class TimeoutException <: Exception {}

If the implementation of Repository is not satisfying, the mock framework can test Controller without creating dependencies. For example, the repository is implemented in another package due to existence of complex dependencies, or the test is too slow.

Testing the findData method:

//Import the mock framework package.
import std.unittest.mock.*
import std.unittest.mock.mockmacro.*

@Test
class ControllerTest {
    let testId: UInt64 = 100
    let testResponse = "foo"

    @TestCase
    func testFindSuccessfully() {
        //Only mocks, rather than a real repository, need to be created.
        let repository = mock<Repository>()

        //Use the @On macro to configure the testData behavior.
        @On(repository.requestData(testId, _)).returns(testResponse)

        //Create a real Controller test for testing an actual implementation.
        let controller = Controller(repository)

        //Run test code.
        let result = controller.findData(testId)

        //Run an assertion on the result.
        @Assert(result == Some(testResponse))
    }

    @TestCase
    func testTimeout() {
        let repository = mock<Repository>()

        //Set getData to throw an exception.
        @On(repository.requestData(testId, _)).throws(TimeoutException())

        let controller = Controller(repository)

        //When an underlying implementation throws an exception, the behavior is tested.
        let result = controller.findData(testId)

        //Run an assertion on the result.
        @Assert(result == None)
    }
}