虽然见到过很多次 fake,stub,mock 这些单词,但一直对它们之间的区分比较混乱,前几天看到 @htmlbiji 在一篇文章中注明三者的中文译名后,觉得不能再混乱下去了,于是搜了一下,结果在 TotT 中找到这篇简短而又清晰的文章,在此分享给大家。
原文地址:http://googletesting.blogspot.com/2013/07/testing-on-toilet-know-your-test-doubles.html
原文作者:Andrew Trenk
译文作者:chenhengjie123
译者注:Testing on the Toilet (TotT) 是 google 的一个测试文章系列。文章内容都比较简短,在一页 A4 纸以内,可以很方便地被打印出来并贴到厕所的海报栏中。
test double (网上找到的中文译名为测试替代,但考虑到英文名称更为通用,所以后文均以英文原名表示) 是一个用于在测试中代替一个真实对象的对象,类似于电影中特技表演时使用的替身。这些概念在大部分情况下被认为是 "mocks(模拟)",但由于不同的 test double 用于不同的地方,因此学会区分它们很重要。 最常见的 test double 是 stubs(桩),mocks(模拟)和 fakes(伪)
stub 没有逻辑,仅返回你让它返回的内容。 当你需要一个对象返回一个特定的值以让你的代码在一个确定的环境下运行时,你需要使用 stub。虽然手写一个 stub 非常简单,但使用一个 moking framework(指用于创建模拟对象的框架)来创建 stub 是一个更方便的方式。
// 传入一个由 moking framework 创建的 stub
AccessManager accessManager = new AccessManager(stubAuthenticationService);
// 在 authentication 服务返回 false 时,用户不应该拥有访问权限
when(stubAuthenticationService.isAuthenticated(USER_ID)).thenReturn(false);
assertFalse(accessManager.userHasAccess(USER_ID));
// 在 authentication 服务返回 true 时,用户应该拥有访问权限
when(stubAuthenticationService.isAuthenticated(USER_ID)).thenReturn(true);
assertTrue(accessManager.userHasAccess(USER_ID));
mock 拥有指定的调用方式,并且如果调用 mock 的方式有错误,测试应该 fail 。 Mock 用于测试对象之间的交互,并且在没有其他可以验证的可见的状态变化或返回值时相当有用。例如:如果你的代码从磁盘读取数据,并且你想确认它仅仅读取了一次磁盘,你可以使用 mock 来验证这个用来读取文件的方法是否只被调用了一次。
// 传入一个由 moking framework 创建的 mock
AccessManager accessManager = new AccessManager(mockAuthenticationService);
accessManager.userHasAccess(USER_ID);
// 如果 accessManager.userHasAccess(USER_ID) 没有调用 mockAuthenticationService.isAuthenticated(USER_ID) 或者调用它不止一次,这个测试应该会 fail
verify(mockAuthenticationService).isAuthenticated(USER_ID);
fake 并不使用 moking framework : 它是一个轻量级的、实现一个和真实模块具有一样行为的 API,但并不适用于生产环境,例如:一个存在内存里面的数据库。 Fake 可以在你不能在你的测试中使用真实模块的情况下使用,例如:真实模块太慢或者它需要通过网络通讯。你不需要经常编写你自己的 fake ,因为 fake 应该被编写真实模块的人/团队进行创建和维护。
// 创建 fake 相当快速和简单
AuthenticationService fakeAuthenticationService = new FakeAuthenticationService();
AccessManager accessManager = new AccessManager(fakeAuthenticationService);
// 由于用户未被添加到 authentication 服务中,用户不应该拥有访问权限
assertFalse(accessManager.userHasAccess(USER_ID));
// 当用户被添加到 authentication 服务中后,用户应该拥有访问权限
fakeAuthenticationService.addAuthenticatedUser(USER_ID);
assertTrue(accessManager.userHasAccess(USER_ID));
"test double" 的概念是被 Gerard Meszaros 在《xUnit Test Patterns》(xUnit 测试模式)书中被提出的。你可以在该书、或者该书的网站中找到更多关于 test double 的信息。你同时可以在这篇由 Martin Fowler 编写的文章中找到不同类型的 test double 的讨论。