type
status
date
slug
summary
tags
category
icon
password
某些情况下,单元测试可能会依赖需要从线上 Web 服务或数据库中获取数据的类。这样会带来一些不便,原因如下:
- 访问线上服务或数据库会拖慢测试执行效率。
- 原本可以通过的测试可能会失败,因为 Web 服务或数据库可能会返回不符合预期的结果。这种情况被称作“flaky test”。
- 使用线上 web 服务或数据库来测试很难覆盖全所有可能成功或失败的场景。
因此,最好不要依赖线上 web 服务或数据库,我们可以把这些依赖“模拟(mock)”出来。模拟(Mocks)允许我们仿造一个线上服务或数据库,并且可以根据条件返回特定结果。
通常来说,可以通过创建类的另一种实现来模拟(mock)这种依赖。类的另一种实现可以手写,也可以借助 Mockito 包,后者简单一些。
本篇教程介绍了 Mockito 包的基本用法,可以参考以下步骤:
- 添加
mockito
和test
依赖
- 创建一个要测试的函数
- 创建一个模拟了
http.Client
的测试文件
- 给每一个条件写一个测试
- 执行这些测试
添加 package 依赖
为了使用 mockito 包,首先将其和
flutter_test
的依赖一起添加到 pubspec.yaml
文件的 dev_dependencies
部分:本例中还使用了
http
包,需要添加到 dependencies
部分:感谢代码生成,
mockito: 5.0.0
已经支持了 Dart 的空安全。要运行所需的代码生成工具,请将 build_runner
依赖添加到 dev_dependencies
项目下。运行
flutter pub add
添加依赖:创建一个要测试的函数
本例中,我们要对 获取网络数据 章节的
fetchAlbum
函数进行单元测试。为了便于测试,我们需要做两个改动:- 给函数提供一个
http.Client
。这样的话我们可以在不同情形下提供相应的http.Client
实例。如果是 Flutter 以及服务端项目,可以提供http.IOClient
。如果是浏览器应用,可以提供http.BrowserClient
。为了测试,我们要提供一个模拟的http.Client
。
- 使用上面提供的
client
来请求网络数据,不要用http.get()
这个静态方法,因为它比较难以模拟。
函数经过改动之后:
在的应用程序代码中,可以使用
fetchAlbum(http.Client())
直接向fetchAlbum
方法提供http.Client
。 http.Client()
创建默认的http.Client
。使用模拟 http.Client 创建测试文件
接下来,创建一个测试文件。
在 main 函数上添加一个
@GenerateMocks([http.Client])
注解以生成含有 mockito
的 MockClient
类。MockClient
类实现了 http.Client
类。如此一来,我们就可以把 MockClient
传给 fetchPost
函数,还可以在每个测试中返回不同的 http 请求结果。生成的 mock 文件将会放在
fetch_album_test.mocks.dart
,请导入以使用它。接下来,运行以下命令生成模拟:
为每个条件编写一个测试
回过头来看,
fetchPost()
函数会完成下面两件事中的一件:- 如果 http 请求成功,返回
Post
- 如果 http 请求失败,抛出
Exception
因此,我们要测试这两种条件。可以使用
MockClient
类为成功的测试返回一个 "OK" 的请求结果,为不成功的测试返回一个错误的请求结果。我们使用 Mockito 的
when()
函数来达到以上目的:运行测试
现在已经有了fetchAlbum()函数并进行了适当的测试,请运行测试。
你也可以参考 单元测试介绍 章节用自己喜欢的编辑器来执行测试。
完整示例
lib/main.dart:
test/fetch_album_test.dart
总结
通过本例,我们已经学会了如何用 Mockito 来测试对 web 服务或数据库有依赖的函数或类。这里只是简短地介绍了 Mockito 库以及模拟(mocking)的概念。更多内容请移步至 Mockito package。