给定以下包含Task 的方法。
self.interactor被嘲笑。func submitButtonPressed() {
Task {
await self.interactor?.fetchSections()
}
}
Run Code Online (Sandbox Code Playgroud)
我如何编写一个测试来验证 fetchSections ()是从该方法调用的?!
我的第一个想法是使用期望并等待它被实现(在模拟的代码中)。
但是对于新的 async/await 有没有更好的方法呢?
小智 12
聚会迟到了,但这对我有用
let task = Task {
sut.submitButtonPressed()
}
await task.value
XCTAssertTrue(mockInteractor.fetchSectionsWasCalled)
Run Code Online (Sandbox Code Playgroud)
我不知道您是否已经找到问题的解决方案,但这是我对面临同样问题的其他开发人员的贡献。
我的情况和你一样,我通过使用Combine通知被测试的类该方法被调用来解决了这个问题。
假设我们有这个方法来测试:
func submitButtonPressed() {
Task {
await self.interactor?.fetchSections()
}
}
Run Code Online (Sandbox Code Playgroud)
我们应该从模拟交互开始:
import Combine
final class MockedInteractor: ObservableObject, SomeInteractorProtocol {
@Published private(set) var fetchSectionsIsCalled = false
func fetchSection async {
fetchSectionsIsCalled = true
// Do some other mocking if needed
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们有了模拟的交互器,我们可以开始编写单元测试:
import XCTest
import Combine
@testable import YOUR_TARGET
class MyClassTest: XCTestCase {
var mockedInteractor: MockedInteractor!
var myClass: MyClass!
private var cancellable = Set<AnyCancellable>()
override func setUpWithError() throws {
mockedInteractor = .init()
// the interactor should be injected
myClass = .init(interactor: mockedInteractor)
}
override func tearDownWithError() throws {
mockedInteractor = nil
myClass = nil
}
func test_submitButtonPressed_should_callFetchSections_when_Always(){
//arrage
let methodCallExpectation = XCTestExpectation()
interactor.$fetchSectionsIsCalled
.sink { isCalled in
if isCalled {
methodCallExpectation.fulfill()
}
}
.store(in: &cancellable)
//acte
myClass.submitButtonPressed()
wait(for: [methodCallExpectation], timeout: 1)
//assert
XCTAssertTrue(interactor.fetchSectionsIsCalled)
}
Run Code Online (Sandbox Code Playgroud)
这里建议了一种解决方案(@andy),涉及注入Task. 有一种方法可以通过func执行返回Task并允许测试 的await任务来做到这一点value。
(我并不热衷于更改可测试的类以适应测试(返回Task),但它允许在async没有NSPredicate或设置一些任意期望时间(这只是气味)的情况下进行测试)。
@discardableResult
func submitButtonPressed() -> Task<Void, Error> {
Task { // I'm allowed to omit the return here, but it's returning the Task
await self.interactor?.fetchSections()
}
}
// Test
func testSubmitButtonPressed() async throws {
let interactor = MockInteractor()
let task = manager.submitButtonPressed()
try await task.value
XCTAssertEqual(interactor.sections.count, 4)
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,正如您所暗示的,您interactor将使用协议进行声明,以便您可以替换模拟以用于测试目的。然后,您查阅模拟对象以确认所需的方法已被调用。通过这种方式,您可以正确限制被测系统的范围,以仅回答“这个方法被调用了吗?”的问题。
至于测试方法本身的结构,是的,这仍然是异步代码,因此需要异步测试。所以使用期望并等待它是正确的。您的应用程序使用 async/await 来表达异步性这一事实并没有神奇地改变这一点!(您可以通过编写一个创建 BOOL 谓词期望并等待它的实用方法来减少其冗长性。)
| 归档时间: |
|
| 查看次数: |
8726 次 |
| 最近记录: |