Как сделать модульный тест Asyncstream функциональностьIOS

Программируем под IOS
Ответить
Anonymous
 Как сделать модульный тест Asyncstream функциональность

Сообщение Anonymous »

У меня есть простая модель просмотра, как это: < /p>
import Foundation
import SwiftUI

typealias MessagePollResult = Result

@MainActor
final class ChatScreenViewModel: ObservableObject {
@Published var messages: [ChatBubbleViewParams] = []
@Published var toast: ToastMessage?

private let messageProvider: MessageProviderUseCase

private var streamTask: Task?

init(messageProvider: MessageProviderUseCase) {
self.messageProvider = messageProvider
}

func onAppear() {
streamTask = Task {
for await messages in await messageProvider.poll(interval: 0.2) {
if case let .success(messages) = messages {
self.messages = messages.map(ChatBubbleViewParams.init)
} else {
self.toast = ToastMessage(text: "Failed to poll messages...")
}
}
}
}
}

protocol MessageProviderUseCase: Sendable {
func poll(interval: TimeInterval) async -> AsyncStream
}

enum MessageParticipantType: Equatable {
case sender
case recipient
}

struct Message: Equatable, Sendable {
let id: String
let participant: User
let participantType: MessageParticipantType
let content: String
let timestamp: Date
}

public enum UserAvatarType: Equatable, Sendable {
case url(URL?)
case local(Image)
}

public struct User: Equatable, Sendable {
let id: String
let name: String
let description: String
let avatar: UserAvatarType?
}

struct ChatBubbleViewParams: Identifiable, Equatable, Hashable {
let id: String
let type: MessageParticipantType
let name: String
let description: String
let avatar: UserAvatarType?
let content: String //NOTE: Need NSAttributedString or not?
let dateTimeString: String

init(from message: Message) {
self.id = message.id
self.type = message.participantType
self.name = message.participant.name
self.description = message.participant.description
self.avatar = message.participant.avatar
self.content = message.content
self.dateTimeString = dateFormatter.string(from: message.timestamp)
}

func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(content)
}

private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd MMM yy, HH:mm"
return formatter
}()
}

struct ToastMessage: Hashable, Identifiable, Equatable {
let id = UUID()
let text: String
let duration: TimeInterval
let position: VerticalAlignment
let bgColor: Color
let textColor: Color

init(text: String,
duration: TimeInterval = 2,
position: VerticalAlignment = .top,
bgColor: Color = Color.red,
textColor: Color = Color.black) {
self.text = text
self.duration = duration
self.position = position
self.bgColor = bgColor
self.textColor = textColor
}

static func == (lhs: ToastMessage, rhs: ToastMessage) -> Bool {
lhs.id == rhs.id
}
}

extension VerticalAlignment : Hashable {
public func hash(into hasher: inout Hasher) {

}
}
< /code>
И теперь я пытаюсь проверить, работает ли функция опроса или нет. Но они красные, возможно, потому, что я мог бы установить неправильные тесты, видя, что сама реальная реализация на самом деле работает нормально. Вот один из тестов: < /p>
@MainActor
struct ChatScreenViewModelTests {

@Test func onAppearShouldReturnInitialMessagesAndStartPolling() async throws {
let mockMessageProvider = MockMessageProvider()
let sut = createSUT(messageProvider: mockMessageProvider)
let cancellable: AnyCancellable?
var messages: [ChatMessages] = []

defer {
cancellable?.cancel()
#expect(messages[0].count == 0)
#expect(messages[1].count > 0)
#expect(mockMessageProvider.pollCallCount == 1)
}

cancellable = sut.$messages.sink { incoming in
messages.append(incoming)
}

sut.onAppear()
mockMessageProvider.emit(MessagePollResult.success(fakeMessages)) //Emit initial messages

}

//- MARK: Helper funcs
private func createSUT(
messageProvider: MessageProviderUseCase = MockMessageProvider()) -> ChatScreenViewModel {
return ChatScreenViewModel(messageProvider: messageProvider)
}
}

private class MockMessageProvider: MessageProviderUseCase {
private var continuation: AsyncStream.Continuation?
private(set) var pollCallCount: Int = 0
private(set) var pollIntervalInput: TimeInterval?

func poll(interval: TimeInterval) async -> AsyncStream {
pollCallCount += 1
pollIntervalInput = interval
return AsyncStream { continuation in
self.continuation = continuation
}
}

func emit(_ value: MessagePollResult) {
continuation?.yield(value)
}

func finish() {
continuation?.finish()
}
}

let fakeSender = User(id: UUID().uuidString,
name: "Fulan Doe",
description: "Test sender",
avatar: .url(senderAvatarUrl))

let fakeRecipient = User(id: UUID().uuidString,
name: "Fulanah Doe",
description: "Test recipient",
avatar: .url(recipientAvatarUrl))

private let fakeMessages = [
Message(
id: UUID().uuidString,
participant: fakeSender,
participantType: .sender,
content: "Hi! How are you? I am Fulan. What's your name?",
timestamp: Date()
),
Message(
id: UUID().uuidString,
participant: fakeRecipient,
participantType: .recipient,
content: "Hi Fulan! I'm fine thanks. My name is Fulanah. Nice to meet you! What are you doing right now?",
timestamp: getFakeDateFromNow(minutesAddition: 5)
)
]

< /code>
Я добавил несколько консольных отпечатков, и оказывается, что Mockmessageprovider никогда ничего не издает. Что я сделал здесь не так? Спасибо.

Подробнее здесь: https://stackoverflow.com/questions/797 ... ctionality
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»