Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[CVW-023] 모듈 테스트 코드 수정 및 CI워크플로우 구축 #35

Merged
merged 7 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 0 additions & 123 deletions Projects/Data/Tests/WebSocketServiceTests.swift
Original file line number Diff line number Diff line change
@@ -1,124 +1 @@
import XCTest
@testable import DataSource

class WebSocketServiceTests: XCTestCase {

// MARK: 웹소켓 연결/해제 테스트
// func testWebSocketConnectAndDisconnect() throws {
//
// // Expectation for WebSocket connection
// let connectExpectation = expectation(description: "WebSocket should connect successfully")
// let disconnectExpectation = expectation(description: "WebSocket should disconnect successfully")
//
// let testURL = URL(string: "wss://stream.binance.com:443/stream")!
//
// // 웹소켓 연결 테스트
// webSocketService.connect(to: testURL, streams: ["btcusdt@trade"]) { message in
// switch message {
// case .string(let text):
// print("Received message: \(text)")
// case .data(let data):
// print("Received data: \(data)")
// @unknown default:
// XCTFail("Unexpected message type")
// }
// }
//
// // WebSocket 연결 이벤트를 수신 대기
// DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
// // 연결이 성공했다면
// if self.webSocketService.task?.state == .running {
// connectExpectation.fulfill()
// } else {
// XCTFail("웹소켓 연결 실패")
// }
// }
//
// // 2초 후에 연결 해제 테스트
// DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [webSocketService] in
//
// webSocketService?.disconnect()
//
// // WebSocket 해제 이벤트 대기
// DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
// if webSocketService?.task?.state == .canceling || webSocketService?.task?.state == .completed {
// disconnectExpectation.fulfill()
// } else {
// XCTFail("웹소켓 연결 해제 실패")
// }
// }
// }
//
// // Test timeouts
// wait(for: [connectExpectation, disconnectExpectation], timeout: 10.0)
// }

// MARK: 웹소켓 구독/구독해제 테스트
// func testWebSocketSubscribeAndUnsubscribe() throws {
//
// // Expectation for WebSocket connection
// let connectExpectation = expectation(description: "WebSocket should connect successfully")
// let disconnectExpectation = expectation(description: "WebSocket should disconnect successfully")
//
// let testURL = URL(string: "wss://stream.binance.com:443/stream")!
//
// // 웹소켓 연결 테스트
// webSocketService.connect(to: testURL) { message in
// switch message {
// case .string(let text):
// print("Received message: \(text)")
// case .data(let data):
// print("Received data: \(data)")
// @unknown default:
// XCTFail("Unexpected message type")
// }
// }
//
// // WebSocket 연결 이벤트를 수신 대기
// DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [webSocketService] in
// // 연결이 성공했다면
// if self.webSocketService.task?.state == .running {
//
// connectExpectation.fulfill()
//
// // 구독 등록
// webSocketService?.subsribe(to: ["btcusdt@trade", "btcusdt@depth"]) { error in
//
// XCTFail("구독 메세지 수신 실패")
// }
//
// // 구독 해제
// DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [webSocketService] in
//
// // 구독 등록
// webSocketService?.unsubscribe(from: ["btcusdt@trade"]) { error in
//
// XCTFail("구독해제 메세지 송신 실패")
// }
// }
//
// } else {
//
// XCTFail("웹소켓 연결 실패")
// }
// }
//
// // 2초 후에 연결 해제 테스트
// DispatchQueue.main.asyncAfter(deadline: .now() + 12) { [webSocketService] in
//
// webSocketService?.disconnect()
//
// // WebSocket 해제 이벤트 대기
// DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
// if webSocketService?.task?.state == .canceling || webSocketService?.task?.state == .completed {
// disconnectExpectation.fulfill()
// } else {
// XCTFail("웹소켓 연결 해제 실패")
// }
// }
// }
//
// // Test timeouts
// wait(for: [connectExpectation, disconnectExpectation], timeout: 15.0)
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import CoreUtil

public class DefaultAllMarketTickersUseCase: AllMarketTickersUseCase {

@Injected private var repository: AllMarketTickersRepository
@Injected private var allMarketTickersRepository: AllMarketTickersRepository

private let throttleTimerQueue: DispatchQueue = .init(
label: "com.AllMarketTickersUseCase",
Expand All @@ -26,7 +26,7 @@ public class DefaultAllMarketTickersUseCase: AllMarketTickersUseCase {

public func requestTickers() -> AnyPublisher<[Twenty4HourTickerForSymbolVO], Never> {

repository
allMarketTickersRepository
.request24hTickerForAllSymbols()
.throttle(for: 0.3, scheduler: throttleTimerQueue, latest: true)
.map { [standardSymbol] fetchedTickers in
Expand Down
41 changes: 0 additions & 41 deletions Projects/Domain/Testing/MockRepository/AllMarketTickers.swift

This file was deleted.

27 changes: 27 additions & 0 deletions Projects/Domain/Testing/StubAllMarketTickersRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// StubAllMarketTickersRepository.swift
// Domain
//
// Created by choijunios on 11/2/24.
//

import Combine
import Foundation

import DomainInterface
import CoreUtil

class StubAllMarketTickersRepository: AllMarketTickersRepository {

let stubTickerEntities: [Twenty4HourTickerForSymbolVO]

init(stubTickerEntities: [Twenty4HourTickerForSymbolVO]) {
self.stubTickerEntities = stubTickerEntities
}

func request24hTickerForAllSymbols() -> AnyPublisher<[Twenty4HourTickerForSymbolVO], Never> {

return Just(stubTickerEntities).eraseToAnyPublisher()
}
}

90 changes: 50 additions & 40 deletions Projects/Domain/Tests/AllMarketTickerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,67 @@ import DomainInterface
import Domain
import CoreUtil

import DomainTesting
@testable import DomainTesting


// MARK: AllMarketTickerUseCaseTests
struct AllMarketTickerUseCaseTests {

let allMarketTickersUseCase: DefaultAllMarketTickersUseCase

init() {
func checkSortedArrayIsReturning() async {

/// 테스트 레포지토리는 "symbol+index"이름을 가지는 정보들을 반환합니다.
/// index의 범위는 0..<100입니다.
/// 해당 레퍼지토리가 반환하는 데이터의 프로퍼티 값은 인덱스에 비례합니다.
DependencyInjector.shared.register(AllMarketTickersRepository.self, MockAllMarketTickersRepository())
// Given
let usdtTickers = (0..<30).map { index in
Twenty4HourTickerForSymbolVO(
pairSymbol: "symbol\(index)USDT",
price: .init(100.0),
totalTradedQuoteAssetVolume: .init(100 + index)!,
changedPercent: .init(50.0)
)
}
let anonymousTickers = (0..<30).map { index in
Twenty4HourTickerForSymbolVO(
pairSymbol: "symbol\(index)CJY",
price: .init(100.0),
totalTradedQuoteAssetVolume: .init(100 + index)!,
changedPercent: .init(50.0)
)
}
let givenTickers = usdtTickers + anonymousTickers

self.allMarketTickersUseCase = .init()
}

@Test
func fromTheBeginTest() async {
DependencyInjector.shared.register(
AllMarketTickersRepository.self,
StubAllMarketTickersRepository(stubTickerEntities: givenTickers)
)
let defaultAllMarketTickersUseCase = DefaultAllMarketTickersUseCase()


for await list in allMarketTickersUseCase
.requestTickers(
maxCount: 5,
cuttingStyle: .begining,
sortOperator: {
$0.price < $1.price
}
).values {
// When
// 티커 요청시
for await fetchedTickers in defaultAllMarketTickersUseCase.requestTickers().values {

let symbols = list.map({ $0.symbol })
// Then

// #1. 30개가 반홨됬는지 확인합니다.
#expect(fetchedTickers.count <= 30)


// #2. 30개의 티커 객체가 USDT를 포함하는 지 확인합니다.
fetchedTickers.forEach { ticker in
#expect(ticker.secondSymbol == "USDT")
}

#expect(symbols == ["symbol0", "symbol1", "symbol2", "symbol3", "symbol4"])
}
}


@Test
func fromTheEndTest() async {

for await list in allMarketTickersUseCase
.requestTickers(
maxCount: 5,
cuttingStyle: .end,
sortOperator: {
$0.price < $1.price
}
).values {

let symbols = list.map({ $0.symbol })
// #3. totalTradedQuoteAssetVolume을 기준으로 올바르게 정렬됬는지 확인합니다.
let expectedTickers = fetchedTickers.sorted {
$0.totalTradedQuoteAssetVolume > $1.totalTradedQuoteAssetVolume
}
for index in 0..<fetchedTickers.count {
#expect(
expectedTickers[index].totalTradedQuoteAssetVolume ==
fetchedTickers[index].totalTradedQuoteAssetVolume
)
}

#expect(symbols == ["symbol95", "symbol96", "symbol97", "symbol98", "symbol99"])
}
}
}
39 changes: 39 additions & 0 deletions Projects/Features/AllMarketTicker/Testing/FakeI18NManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// FakeI18NManager.swift
// AllMarketTickerModule
//
// Created by choijunios on 1/16/25.
//

import Combine

import I18N
import DomainInterface

class FakeI18NManager: I18NManager {

let fakeUserConfigRepository = FakeUserConfigurationRepository()
private let changePublisher: PassthroughSubject<I18NConfigMutation, Never> = .init()

public func getChangePublisher() -> AnyPublisher<I18NConfigMutation, Never> {
changePublisher.eraseToAnyPublisher()
}

func getCurrencyType() -> DomainInterface.CurrencyType {
fakeUserConfigRepository.getCurrencyType()
}

func setCurrencyType(type: DomainInterface.CurrencyType) {
fakeUserConfigRepository.setCurrencyType(type: type)
changePublisher.send(.init(currencyType: type))
}

func getLanguageType() -> DomainInterface.LanguageType {
fakeUserConfigRepository.getLanguageType()
}

func setLanguageType(type: DomainInterface.LanguageType) {
fakeUserConfigRepository.setLanguageType(type: type)
changePublisher.send(.init(languageType: type))
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//
// StubUserConfigurationRepository.swift
// FakeUserConfigurationRepository.swift
// AllMarketTickerModule
//
// Created by choijunios on 1/13/25.
//

import DomainInterface

class StubUserConfigurationRepository: UserConfigurationRepository {
class FakeUserConfigurationRepository: UserConfigurationRepository {

private var fakeDB: [String: String] = [
UserConfiguration.currency.savingKey: CurrencyType.dollar.savingValue,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// StubExchangeUseCase.swift
// AllMarketTickerModule
//
// Created by choijunios on 1/16/25.
//

import Combine

import DomainInterface

class StubExchangeUseCase: ExchangeRateUseCase {

func getExchangeRate(base: DomainInterface.CurrencyType, to: DomainInterface.CurrencyType) -> AnyPublisher<Double, Never> {

return Just(1.0).eraseToAnyPublisher()
}

func prepare() { }
}
Loading