如何创建触发函数时通过的测试?
Posted
技术标签:
【中文标题】如何创建触发函数时通过的测试?【英文标题】:How to create a test that passes when a function is triggered? 【发布时间】:2018-12-01 05:44:52 【问题描述】:我正在尝试对视图控制器类中的两个函数进行单元测试。这两个函数将分别创建一个用户和登录一个用户。我的测试与 UI 无关。
到目前为止,我只需要创建一个在调用其中一个函数时通过的测试。目标是尽可能在不更改当前视图控制器实现的情况下执行此操作,并将其全部保存在测试类/函数中。
我该怎么做?
谢谢
【问题讨论】:
欢迎来到 Stack Overflow!尽管我很想了解最新的模拟工具(我仍然手动模拟),但 SO 不是基于意见的问题的论坛。这就是为什么你会看到负分,问题最终被关闭。 …相反,SO 专注于特定的编程问题以及这些问题的解决方案。 好吧,对不起!将适当地重组问题。 @JonReid 好的,刚刚编辑它以使其成为一个特定的编程问题!如果它仍然不适合 SO,请告诉我,我会再次编辑它! 我收回我所说的“问题最终被关闭”——干得好。 您是否正在寻找一个验证“创建用户”的测试被调用,另一个测试来验证“登录用户”? 【参考方案1】:我假设视图控制器具有调用“创建用户”或“登录用户”的方法。 (你说它与 UI 无关,但如果这是触发器,我们可以轻松测试按钮点击。)
class MyViewController: UIViewController
func triggeringMethod()
// Calls one of the two methods below
func createUser()
// Does stuff
func signInUser()
// Does stuff
听起来你想测试流程,而不是效果。也就是说,您要测试“是否调用了createUser()
?”有几种方法可以得到你想要的,但更好的方法需要你改变你的视图控制器实现。
制作部分模拟
Michael Feathers 的 Working Effectively With Legacy Code 中的一个标准技巧是“子类和覆盖方法”。让我们从那里开始。在测试代码中,我们可以制作
class TestableMyViewController: MyViewController
override func createUser()
override func signInUser()
到目前为止,这是一种消除这些方法影响的方法。但是我们现在可以从我的尝试中添加模拟技术! Swift 东京谈话。
class TestableMyViewController: MyViewController
var createUserCallCount = 0
var signInUserCallCount = 0
override func createUser()
createUserCallCount += 1
override func signInUser()
signInUserCallCount += 1
现在可以调用触发方法,查看调用次数了。
(您可能需要做的更改:类不能是final
。方法不能是private
。)
移动工人
虽然这是一个很好的起点,但不要止步于此。我们创建的是“部分模拟”。那是我们保留大部分功能的地方,但模拟了几个方法。这是要避免的。原因是我们最终得到了混合生产代码和测试代码的类。如果您无意中测试了测试代码而不是测试生产代码,这太容易结束了。
部分模拟清楚地表明我们缺少一个边界。视图控制器做的太多了。 “创建用户”和“登录用户”的实际工作应该由另一种类型(甚至可能是两种类型)执行。在 Swift 中,我们可以使用协议来定义这个边界。这样生产代码可以使用真正的功能,而对于测试代码我们可以注入模拟。
这意味着生产代码应避免自行决定由谁来执行实际工作。相反,我们应该告诉它是谁做的。这样,测试可以提供替代工人。从外部指定这些依赖项称为“依赖注入”。
回传效果
另一个选项让我们完全避免模拟。我们可以在枚举中描述期望的效果,而不是测试是否调用了某些东西。然后我们可以定义效果像
enum Effect
case createUser(CreateUserRequestModel)
case signInUser(SignInUserRequestModel)
它不会调用createUser()
或signInUser()
的触发方法,而是调用委托。 (另一种选择是传入闭包而不是指定委托。)
protocol Delegate
perform(_ effect: Effect)
然后在触发方法中,
delegate?.perform(.createUser(parameters))
这意味着由实际委托将这些枚举值转换为实际工作。但它使测试易于编写。我们所需要的只是提供一个捕获Effect
值的测试实现。
【讨论】:
感谢您的回答!我回家后试试这个! 我明白了,再次感谢您!不过,我确实有一个后续问题:因此,将来在设计注册功能时,我应该有一个模型 User 并且该模型应该有一个注册成员函数。然后在我的视图控制器的注册函数中,我应该传递一个用户对象,然后调用该对象的注册成员函数?这种方式我使用依赖注入+我可以使用协议模式进行测试,是否正确? 是的。我实际上会更进一步,将“用户”模型与“在用户对象上工作的网络请求”分开。换句话说,我会将 User 视为要传递的数据,仅此而已。它可以作为一个请求模型,我在这里描述过:qualitycoding.org/request-model 我明白了。好的,太好了,谢谢!很有帮助的答案。 快速问题(如果我应该将其转换为官方 SO 问题,请告诉我):如果我们有一堆具有一堆模型的遗留代码,而不是所有函数的成员函数在视图控制器中(所以要使用依赖注入,我们必须重构很多代码库),我们应该使用哪种测试方法?我们应该重构并使用依赖注入的模拟吗?以上是关于如何创建触发函数时通过的测试?的主要内容,如果未能解决你的问题,请参考以下文章
Javascript:如何让单选按钮的“onchange”事件通过外部函数触发?
我的 Serverless 实战 — 云函数与触发器的创建与使用 ( 开通腾讯云 “ 云开发 “ 服务 | 创建云函数 | 创建触发器 | 测试触发器 )
在我的S3存储桶上执行getObject操作时,如何触发Lambda函数?