我是使用依賴注入的新手。當我使用模擬物件運行測驗時,成功結果案例沒有被觸發,并且該物件沒有添加到我@Published要測驗的屬性中。
當我去測驗時,viewModel我可以看到要測驗的陣列,但是當getShows()在測驗中被呼叫時,會Result使用該案例。
測驗檔案
import XCTest
@testable import PopularTVViewer
class PopularTVViewerTests: XCTestCase {
func testPopularTVModel() {
let mockedManager = MockedPopularTVManager()
mockedManager.result = .success(mockedManager.mockPopularShows)
let viewModel = PopularTVViewModel(manager: mockedManager)
#warning("getShows() success case isn't being called even through viewModel has its reference.")
viewModel.getShows()
XCTAssertNotNil(viewModel)
XCTAssertNotNil(mockedManager.result)
// Currently failing
// XCTAssertEqual(viewModel.popularTV.count, 4)
XCTAssertEqual(mockedManager.getPopularShowsCallCounter, 1)
}
}
嘲笑經理
class MockedPopularTVManager: PopularTVManagerProtocol {
var result: Result<[PopularTV], NetworkError>!
var getPopularShowsCallCounter = 0
func getPopularShows(completion: @escaping (Result<[PopularTV], NetworkError>) -> Void) {
completion(result)
getPopularShowsCallCounter = 1
}
let mockPopularShow = PopularTV(name: "House of the Dragon", posterPath: "/mYLOqiStMxDK3fYZFirgrMt8z5d.jpg", popularity: 4987.163, voteAverage: 7.7, voteCount: 881)
let mockPopularShows = [
PopularTV(name: "The Lord of the Rings: The Rings of Power", posterPath: "/mYLOqiStMxDK3fYZFirgrMt8z5d.jpg", popularity: 4987.163, voteAverage: 7.7, voteCount: 881),
PopularTV(name: "House of the Dragon", posterPath: "/z2yahl2uefxDCl0nogcRBstwruJ.jpg", popularity: 4979.127, voteAverage: 8.6, voteCount: 1513),
PopularTV(name: "She-Hulk: Attorney at Law", posterPath: "/hJfI6AGrmr4uSHRccfJuSsapvOb.jpg", popularity: 2823.739, voteAverage: 7.1, voteCount: 846),
PopularTV(name: "Dahmer – Monster: The Jeffrey Dahmer Story", posterPath: "/f2PVrphK0u81ES256lw3oAZuF3x.jpg", popularity: 1774.56, voteAverage: 8.3, voteCount: 402)
]
}
視圖模型
final class PopularTVViewModel: ObservableObject {
@Published var popularTV = [PopularTV]()
let manager: PopularTVManagerProtocol
let columns = ColumnLayoutHelper.threeColumnLayout
// Injecting for testing.
init(manager: PopularTVManagerProtocol = PopularTVManager()) {
self.manager = manager
}
// Grabbing next page of results
func getMoreShows() {
getShows()
}
// Initial network call.
func getShows() {
manager.getPopularShows() { [weak self] result in
DispatchQueue.main.async {
switch result {
case .success(let popularTV):
for show in popularTV {
self?.popularTV.append(show)
}
case .failure(let error):
switch error {
case .invalidURL:
print("Invalid URL")
case .invalidData:
print("Invalid Data")
case .unableToComplete:
print("Unable to complete")
case .invalidResponse:
print("Invalid response")
}
}
}
}
}
}
我已經做了我能想到的一切來確保物件存在并且viewModel可以訪問它,但它好像mockedManager在我的測驗中運行時沒有成功getShows()。
uj5u.com熱心網友回復:
測驗失敗,因為您在測驗期間切換執行緒。在DispatchQueue.main.async你PopularTVViewModel的導致它失敗。
您需要DispatchQueue.main.async從測驗中洗掉使用。這是可以做到的。
我們首先需要創建一個函式來代替對DispatchQueue.main.async. 該函式將檢查我們是否在主執行緒上,如果我們直接執行代碼而不切換執行緒,否則如果我們在后臺執行緒上,它將在主執行緒上調度。這應該意味著在您的應用程式中它的作業方式與以前完全相同,并且在您的測驗中它避免了執行緒跳躍,因此它們現在通過了。
/// You could make this a global function, an extension on DispatchQueue,
/// the choice where to put it is up to you, but it should be accessible
/// by whichever classes need to use it as chances are you may need to use
/// it in multiple places.
func performUIUpdate(using closure: @escaping () -> Void) {
if Thread.isMainThread {
closure()
} else {
DispatchQueue.main.async(execute: closure)
}
}
然后我們可以更新您PopularTVViewModel以使用新功能。
final class PopularTVViewModel: ObservableObject {
@Published var popularTV = [PopularTV]()
let manager: PopularTVManagerProtocol
let columns = ColumnLayoutHelper.threeColumnLayout
// Injecting for testing.
init(manager: PopularTVManagerProtocol = PopularTVManager()) {
self.manager = manager
}
// Grabbing next page of results
func getMoreShows() {
getShows()
}
// Initial network call.
func getShows() {
manager.getPopularShows() { [weak self] result in
performUIUpdate { // Note we use the new function here instead of DispatchQueue.main.async
switch result {
case .success(let popularTV):
// you could use the following instead of your for loop.
// self?.popularTV.append(contentsOf: popularTV)
for show in popularTV {
self?.popularTV.append(show)
}
case .failure(let error):
switch error {
case .invalidURL:
print("Invalid URL")
case .invalidData:
print("Invalid Data")
case .unableToComplete:
print("Unable to complete")
case .invalidResponse:
print("Invalid response")
}
}
}
}
}
}
您的測驗現在應該通過了。
John Sundell有一篇很棒的文章展示了如何減少測驗中的不穩定。
Jon Reid 的這本書,iOS Unit Testing by Example也非常好,值得放在你的書架上。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/523262.html
標籤:IOS迅速单元测试测试
上一篇:如何單元測驗水線模型創建方法實體
