xcode 访问sut对象时,PassthrowSubject的Swift XC UnitTest崩溃

bpsygsoo  于 2023-02-16  发布在  Swift
关注(0)|答案(1)|浏览(84)

我是iOS开发单元测试的新手,我有一个如下的视图模型

class PostsViewViewModel {
    private let serviceRequest: NetworkRequestProtocol
    public private(set) var requestOutput: PassthroughSubject<RequestOutput, Never> = .init()
 private var cancellables = Set<AnyCancellable>()

    init(
        request: NetworkRequestProtocol,
        user: LoginUserModel,
        codeDataManager: CoreDataManagerProtocol) {
            serviceRequest = request
            loadPostsFromServerFor(user: user)
        }

    private func loadPostsFromServerFor(user: LoginUserModel) {
        Task {
            do {
                let postsRecived = try await serviceRequest.callService(
                    with: ServiceEndPoint.fetchPostsForUser(id: user.userid),
                    model: [PostModel].self,
                    serviceMethod: .get
                )
                if postsRecived.isEmpty {
                    requestOutput.send(.fetchPostsDidSucceedWithEmptyList)
                } else {
                    recievedRawPostsModel = postsRecived
                    createPostModelsFromPostRecieved(postsRecived)
                    requestOutput.send(.fetchPostsDidSucceed)
                }
            } catch {
                requestOutput.send(.fetchPostsDidFail)
            }
        }
    }
}
extension PostsViewViewModel {        
    enum RequestOutput {
        case fetchPostsDidFail
        case fetchPostsDidSucceed
        case fetchPostsDidSucceedWithEmptyList
        case reloadPost
    }
  }

现在我创建了一个ViewModel的测试类,如下所示

final class PostViewViewModelTest: XCTestCase {
    let userInput: PassthroughSubject<PostsViewViewModel.UserInput, Never> = .init()

    private var cancellable = Set<AnyCancellable>()
    private let mockUser = LoginUserModel(userid: 1)
    private let coreDataManager = CoreDataStackInMemory()
    private var sutPostViewModel: PostsViewViewModel!
    
    override func setUp() {
        sutPostViewModel = PostsViewViewModel(
            request: MockNetworkRequestPostSuccess(),
            user: mockUser, codeDataManager: coreDataManager
        )
    }
    
    override func tearDown() {
       sutPostViewModel = nil
    }
    
    func testPostsViewViewModel_WhenPostModelLoaded_NumberOfRowsSouldBeMoreThanZero()  {
//        let postViewModel = sutPostViewModel!

        self.sutPostViewModel.requestOutput
            //.receive(on: DispatchQueue.main)
            .sink { [weak self] output in
                XCTAssertTrue(output == .fetchPostsDidSucceed)
                XCTAssertTrue((self?.sutPostViewModel.numberOfRowsInPostTableView)! > 0)
            }
            .store(in: &cancellable)
    }
    
    func testPostsViewViewModel_WhenPostModelLoaded_GetPostAtGivenIndexPathMustHaveEqualPostID() {
//        let postViewModel = sutPostViewModel!
        self.sutPostViewModel.requestOutput
            .receive(on: DispatchQueue.main)
            .sink { [weak self] output in
            print(output == .reloadPost)
            let post = self?.sutPostViewModel.getPost(at: IndexPath(row: 0, section: 0))
            let postModel: [PostModel] = JSONLoader.load("Posts.json")
            XCTAssertTrue(post.postID == postModel[0].id)
        }
        .store(in: &cancellable)
    }
}

但是测试用例在访问sutpostViewModel时崩溃了。我不明白我在这里做错了什么。
调试时,我发现在接收和测试崩溃之前调用了tearDown()。

dojqjjoe

dojqjjoe1#

我觉得你可能需要一个预期。

func testPostsViewViewModel_WhenPostModelLoaded_NumberOfRowsSouldBeMoreThanZero() {
    let expectation = expectation(description: "Sink Executed") // 1.

    self.sutPostViewModel.requestOutput
       .sink { [weak self] output in
           XCTAssertTrue(output == .fetchPostsDidSucceed)
           XCTAssertTrue((self?.sutPostViewModel.numberOfRowsInPostTableView)! > 0)
           expectation.fulfill() //2. Fulfill to stop waiting
       }
       .store(in: &cancellable)

    wait(for: [expectation], timeout: 5) // 3. Wait for 5 seconds before timeout and failure
}

您有异步代码,因此sink在测试方法运行后执行。此时,tearDown已被调用,sut设置为nil

相关问题