mock 框架入门

使用 mock 框架

mock 框架本身是仓颉标准库中单元测试的一部分。使用 mock 框架前,需将 unittest.mock.*unittest.mock.mockmacro.* 导入到测试文件中。

如果使用 cjpm 工具,仅需运行 cjpm test 命令即可自动启用 mock 框架。

如果直接使用 cjc ,参见使用 cjc

示例

常见 mock 测试用例:

  • 调用mock 构造函数创建 mock/spy 对象。
  • 调用配置 API设置 mock 行为。
  • 使用 mock 对象替代测试代码依赖。
  • (可选)调用验证 API来验证测试代码与 mock/spy 对象之间的交互。

以如下简单API为例:

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 {}

如果 Repository 实现不理想,比如可能包含复杂的依赖关系,实现在其他包中,或者测试太慢,mock 框架可以在不创建依赖的情况下测试 Controller

测试 findData 方法:

//导入mock框架包
import std.unittest.mock.*
import std.unittest.mock.mockmacro.*

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

    @TestCase
    func testFindSuccessfully() {
        //只需要创建mock,不用创建真正的Repository
        let repository = mock<Repository>()

        //使用@On宏配置testData行为
        @On(repository.requestData(testId, _)).returns(testResponse)

        //创建真正的Controller测试以便测试实际的实现
        let controller = Controller(repository)

        //运行测试代码
        let result = controller.findData(testId)

        //对结果运行断言
        @Assert(result == Some(testResponse))
    }

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

        //设置getData抛出异常
        @On(repository.requestData(testId, _)).throws(TimeoutException())

        let controller = Controller(repository)

        //底层实现抛出异常时,测试行为
        let result = controller.findData(testId)

        //对结果运行断言
        @Assert(result == None)
    }
}