在代码中,我是这样做的:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateBadgeValuesForTabBarItems()
}
private func updateBadgeValuesForTabBarItems() {
DispatchQueue.main.async {
self.setBadge(value: self.viewModel.numberOfUnreadMessages, for: .threads)
self.setBadge(value: self.viewModel.numberOfActiveTasks, for: .tasks)
self.setBadge(value: self.viewModel.numberOfUnreadNotifications, for: .notifications)
}
}
在测试中:
func testViewDidAppear() {
let view = TabBarView()
let model = MockTabBarViewModel()
let center = NotificationCenter()
let controller = TabBarController(view: view, viewModel: model, notificationCenter: center)
controller.viewDidLoad()
XCTAssertFalse(model.numberOfActiveTasksWasCalled)
XCTAssertFalse(model.numberOfUnreadMessagesWasCalled)
XCTAssertFalse(model.numberOfUnreadNotificationsWasCalled)
XCTAssertFalse(model.indexForTypeWasCalled)
controller.viewDidAppear(false)
XCTAssertTrue(model.numberOfActiveTasksWasCalled) //failed
XCTAssertTrue(model.numberOfUnreadMessagesWasCalled) //failed
XCTAssertTrue(model.numberOfUnreadNotificationsWasCalled) //failed
XCTAssertTrue(model.indexForTypeWasCalled) //failed
}
但我最近的四个主张都失败了。为什么?我怎样才能成功地测试它?
8条答案
按热度按时间mctunoxg1#
我认为测试这一点的最好方法是模拟
DispatchQueue
。您可以创建定义要使用的功能的协议:现在扩展
DispatchQueue
以符合您的协议,如:注意,我不得不从协议中省略代码中没有使用的参数,比如
group
、qos
和flags
,因为协议不允许默认值。这就是为什么扩展必须显式地实现协议功能。现在,在测试中,创建一个符合该协议的模拟
DispatchQueue
,并同步调用闭包,如:现在,您需要做的就是相应地注入队列,也许在视图控制器的
init
中,就像:最后,在你的测试中,你需要使用模拟的分派队列来创建你的视图控制器,比如:
由于您在测试中使用了模拟队列,代码将同步执行,您不需要沮丧地等待期望。
tmb3ates2#
您不需要在主队列上调用
updateBadgeValuesForTabBarItems
方法中的代码。但如果你真的需要,你可以这样做:
但这不是一个好的做法。
8fq7wneg3#
我在测试中使用了
DispatchQueue.main.asyncAfter()
沿着否则在DispatchQueue.main.async {}
中设置文本之前就会失败测试方法:
测试:
kxe2p93d4#
你应该
1.注入依赖项(DispatchQueue)到视图控制器中,以便在测试中更改
1.使用协议反转依赖,更符合SOLID原则(接口分段和依赖反转)
1.在测试中模拟DispatchQueue,以便您控制场景
让我们应用这三个项目:
为了反转依赖,我们需要一个 abstract 类型,也就是说,在Swift中,一个协议。然后我们扩展DispatchQueue以符合该协议
接下来,我们需要将依赖项注入到视图控制器中。这意味着,将任何正在调度的内容传递给视图控制器
最后,我们需要为我们的测试创建一个mock。在本例中,通过遵循testing doubles,我们应该创建一个Fake,也就是说,一个实际上不能在生产环境中工作但可以在测试中工作的DispatchQueue mock
当我们进行测试时,我们需要做的就是创建我们的SystemUnderTest(在本例中是控制器),传递一个假的调度示例
1hdlvixo5#
你可以通过检查当前线程是否是主线程,并在这种情况下同步执行代码来轻松实现这一点。
例如,在presenter中,我以这种方式更新视图:
然后我可以为演示者编写同步单元测试:
o4hqfura6#
这里是一个小的概念证明,你可以如何实现它:
下面是
MockModel
:xnifntxz7#
要测试异步代码,你应该修改你的
updateBadgeValuesForTabBarItems
函数,并使用completion闭包直接从测试中调用它:现在你可以像以前一样在常规代码中调用这个函数了,例如:
updateBadgeValuesForTabBarItems()
。但是对于测试,你可以添加一个completion闭包并使用XCTestExpectation
来等待:wwodge7n8#
几年后,但也许有用。
在我的特定示例中,我使用
DispatchQueue
上的扩展来检测我们是否正在运行单元测试。因此,如果我们在XCTest
环境中,该方法将自动执行而不进行分派。这样,在我们的生产代码中,我们使用
DispatchQueue.main.asyncTestable { ... }
而不是DispatchQueue.main.async { ... }
。在您的特定场景中:并且您的测试应该在没有修改的情况下正确工作。