У меня есть простая модель просмотра, как это: < /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
Как сделать модульный тест Asyncstream функциональность ⇐ IOS
Программируем под IOS
-
Anonymous
1753694217
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 никогда ничего не издает. Что я сделал здесь не так? Спасибо.
Подробнее здесь: [url]https://stackoverflow.com/questions/79716868/how-to-unit-test-asyncstream-functionality[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия