์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- arkit
- node.js
- Swfit
- Xcode
- visionOS
- realm
- MVVM
- BOJ
- swift
- tableView
- ํ๋ก๊ทธ๋๋จธ์ค
- XCTest
- UIKit
- rxcocoa
- combine
- raywenderlich
- RxSwift
- TCA
- Flutter
- SnapKit
- BFS
- Kuring
- ๋ฐฑ์ค
- SwiftUI
- CollectionView
- designpattern
- ios
- Lv2
- reactorkit
- ํจ์คํธ์บ ํผ์ค
- Today
- Total
lgvv98
part5 (ch1). FindCVS UnitTest ์ฝ๋๋ฆฌ๋ทฐ (feat. Stubber) ๋ณธ๋ฌธ
part5 (ch1). FindCVS UnitTest ์ฝ๋๋ฆฌ๋ทฐ (feat. Stubber)
๐ฅ ์บ๋ฟ๋งจ 2022. 2. 20. 18:08FindCVS UnitTest ์ฝ๋๋ฆฌ๋ทฐ
โ ๊ฐ๋ฐ์ ์๋ฃํ์ผ๋ฉด ์ฝ๋๋ฆฌ๋ทฐ๋ฅผ ํ๋๊ฑด ๋น์ฐ.
ํ ์คํธ๋ฅผ ํ๋ฉด์ ์ฒ์์ผ๋ก XCTest์ ๊ด๋ จํ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด๋ด์ ๋์ฑ ์ง์คํด์ ๋ณด๊ฒ ๋์์.
๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฐ์๋ง๋ค ๋ค๋ฅธ ์คํ์ผ์ ๊ฐ๊ณ ์๋๋ฐ, ๋ณด๋ฉด์ ์ด๋ค ์ ์ด ๋ ๋์์ง ์ค์ค๋ก ๊ณ์ ์๊ฐํ๊ฒ ๋๋ ์๊ฐ์ด๋ผ ์ข์๋ค.
UIํ ์คํธ๋ ์งํํ์ง ์์์.
โ ํ ์คํธ์ฝ๋ ํ์ผ ๊ตฌ์กฐ
๐ LocationInformationModelTest.swift
//
// LocationInformationModelTest.swift
// FindCVSTests
//
// Created by Hamlit Jason on 2022/02/18.
//
import XCTest
import Nimble
@testable import FindCVS
class LocationInformationModelTest: XCTestCase {
let stubNetwork = LocalNetworkStub()
var doc: [KLDocument]!
var model: LocationInformationModel!
override func setUp() {
// ๊ฐ์์ ๋คํธ์ํฌ ๋ชจ๋ธ์ ์ฃผ๊ณ ์์ํ๋ค.
self.model = LocationInformationModel(localNetwork: stubNetwork)
self.doc = cvsList
}
func testdocumentsToCellData() {
let cellData = model.documentsToCellData(doc) // ์ค์ ๋ชจ๋ธ์ ๊ฐ
let placeName = doc.map { $0.placeName } // dummy์ ๊ฐ
let address0 = cellData[1].address// ์ค์ ๋ชจ๋ธ์ ๊ฐ
let roadAddressName = doc[1].roadAddressName // dummy์ ๊ฐ
// ์ค์ ์ ๋์ผํด์ผ ํ๋ค. ์ฆ, ์ธ๋ถ์ ๊ฐ ์์ฒด๊ฐ ๋ฌ๋ผ์ ธ์๋ ์๋๋ค.
expect(cellData.map {$0.placeName}).to(
equal(placeName),
description: "DetailListCellData์ placeName์ document์ PlaceName์ด๋ค")
expect(address0).to(
equal(roadAddressName),
description: "KLDocument์ RoadAddressName์ด ๋น ๊ฐ์ด ์๋๊ฒฝ์ฐ roadAddress๊ฐ CellData์ ์ ๋ฌ๋๋ค.")
}
}
Nimble์ ์ฌ์ฉํ๋๋ฐ, XCTAssertEqual๊ณผ ๋์ผํ ๋ฉ์๋์ด๋ค.
๐ LocalNetworkStub.swift
//
// LocalNetworkStub.swift
// FindCVSTests
//
// Created by Hamlit Jason on 2022/02/20.
//
import Foundation
import Stubber // ๋คํธ์ํฌ ์์์ ๋๋ฌด ๋ณ์๊ฐ ๋ง์ผ๋๊น ๊ฐ์์ ๋คํธ์ํฌ์์ ์ฃผ์
ํ๋๊ฑฐ
import RxSwift
@testable import FindCVS
class LocalNetworkStub: LocalNetwork {
// LocalNetwork ์์ ๋ฐ์์ ์ฌ์ฉํจ.
override func getLocation(by mapPoint: MTMapPoint) -> Single<Result<LocationData, URLError>> {
return Stubber.invoke(getLocation, args: mapPoint)
}
}
Stubber๋ฅผ ํ์ฉํ์ฌ ๋คํธ์ํฌ ์์์ ๋ณ์๊ฐ ๋ง์ผ๋๊น ๋คํธ์ํฌ๋ฅผ ์ฃผ์ ํ๋ ๋ฐฉ๋ฒ
Stubber์ ๋ํด์๋ ๋ฐ๋ก ํฌ์คํ ์ ํด๋ณด์.
๐ LocationInformationViewModelTests.swift
//
// LocationInformationViewModelTests.swift
// FindCVSTests
//
// Created by Bo-Young PARK on 2021/09/26.
//
import XCTest
import Nimble
import RxSwift
import RxTest
@testable import FindCVS
class LocationInformationViewModelTests: XCTestCase {
let disposeBag = DisposeBag()
let stubNetwork = LocalNetworkStub()
var model: LocationInformationModel!
var viewModel: LocationInformationViewModel!
var doc: [KLDocument]!
override func setUp() {
self.model = LocationInformationModel(localNetwork: stubNetwork)
self.viewModel = LocationInformationViewModel(model: model)
self.doc = cvsList
}
func testSetMapCenter() {
let scheduler = TestScheduler(initialClock: 0)
//๋๋ฏธ๋ฐ์ดํฐ ์ด๋ฒคํธ
let dummyDataEvent = scheduler.createHotObservable([
.next(0, cvsList)
])
let documentData = PublishSubject<[KLDocument]>()
dummyDataEvent
.subscribe(documentData)
.disposed(by: disposeBag)
//DetailList ์์ดํ
(์
) ํญ ์ด๋ฒคํธ
let itemSelectedEvent = scheduler.createHotObservable([
.next(1, 0)
])
let itemSelected = PublishSubject<Int>()
itemSelectedEvent
.subscribe(itemSelected)
.disposed(by: disposeBag)
let selectedItemMapPoint = itemSelected
.withLatestFrom(documentData) { $1[0] }
.map(model.documentToMTMapPoint)
//์ต์ด ํ์ฌ ์์น ์ด๋ฒคํธ
let initialMapPoint = MTMapPoint(geoCoord: MTMapPointGeo(latitude: Double(37.394225), longitude: Double(127.110341)))!
let currentLocationEvent = scheduler.createHotObservable([
.next(0, initialMapPoint)
])
let initialCurrentLocation = PublishSubject<MTMapPoint>()
currentLocationEvent
.subscribe(initialCurrentLocation)
.disposed(by: disposeBag)
//ํ์ฌ ์์น ๋ฒํผ ํญ ์ด๋ฒคํธ
let currentLocationButtonTapEvent = scheduler.createHotObservable([
.next(2, Void()),
.next(3, Void())
])
let currentLocationButtonTapped = PublishSubject<Void>()
currentLocationButtonTapEvent
.subscribe(currentLocationButtonTapped)
.disposed(by: disposeBag)
let moveToCurrentLocation = currentLocationButtonTapped
.withLatestFrom(initialCurrentLocation)
// Merge
let currentMapCenter = Observable
.merge(
selectedItemMapPoint,
initialCurrentLocation.take(1),
moveToCurrentLocation
)
let currentMapCenterObserver = scheduler.createObserver(Double.self)
currentMapCenter
.map { $0.mapPointGeo().latitude } // lat,lng ๋๋ค ๊ฒ์ฌ ์๋๋ผ lat๋ง ๊ฒ์ฌ
.subscribe(currentMapCenterObserver)
.disposed(by: disposeBag)
scheduler.start()
let secondMapPoint = model.documentToMTMapPoint(doc[0])
expect(currentMapCenterObserver.events).to(
equal([
.next(0, initialMapPoint.mapPointGeo().latitude),
.next(1, secondMapPoint.mapPointGeo().latitude),
.next(2, initialMapPoint.mapPointGeo().latitude), // ๋ด์์น๋๊น ๊ฒฐ๊ตญ ์ต์ด์์น๋ ๊ฐ์์ผ ํ๋ค.
.next(3, initialMapPoint.mapPointGeo().latitude)
])
)
}
}
์ฃผ์ ์ฝ์ด๋ณด๋ฉด ์ฝ๊ฒ ์ดํดํ ์ ์์ผ๋๊น ์ฒ์ฒํ ์ฝ์ด๋ณด๊ธฐ.
๋ก์ง ์์ฒด๊ฐ ์ด๋ ต์ง๋ ์์๋ฐ, rx๋ฅผ ์ ๋ง ๋ค์ฑ๋กญ๊ฒ ํ์ฉํ๋๊ฑฐ ๊ฐ์์ ๋ ์ด๋ ๊ฒ ๋ฐฐ์๊ฐ ใ ใ
๐ Dummy.swift
//
// Dummy.swift
// FindCVSTests
//
// Created by Bo-Young PARK on 2021/09/26.
//
import Foundation
@testable import FindCVS
var cvsList: [KLDocument] = Dummy().load("networkDummy.json")
class Dummy {
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
let bundle = Bundle(for: type(of: self))
guard let file = bundle.url(forResource: filename, withExtension: nil) else {
fatalError("\(filename)์ main bundle์์ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("\(filename)์ main bundle์์ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค.\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("\(filename)์ \(T.self)๋ก ํ์ฑํ ์ ์์ต๋๋ค.")
}
}
}
cvsList๋ฅผ ์ ์ญ์ผ๋ก ์ค์ ํด ๋์ด์ ์ด๊ฒ๋ ์ธ์์ ์ด๋ค.
load<T: Decodable> ์์ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ๋๋ฐ, ์ด๊ฑฐ ์ฐ์ต ๋ ํด๋ณด์!
๐ networkDummy.json
[
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋ฐฑํ๋ 537",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > ์ด๋งํธ24",
"distance": "2",
"id": "1306112074",
"phone": "031-622-7176",
"place_name": "์ด๋งํธ24 Rํ๊ต์ํ๋2ํธ์ ",
"place_url": "http://place.map.kakao.com/1306112074",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก 152",
"x": "127.110346616745",
"y": "37.394204373845"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋ฐฑํ๋ 132-189",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > GS25",
"distance": "74",
"id": "16486797",
"phone": "031-707-6410",
"place_name": "GS25 Sํ๊ต์ญ์ ",
"place_url": "http://place.map.kakao.com/16486797",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก ์งํ 160",
"x": "127.11079919194681",
"y": "37.39478420869054"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋ฐฑํ๋ 530",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > ์ธ๋ธ์ผ๋ ๋ธ",
"distance": "101",
"id": "1003710555",
"phone": "",
"place_name": "์ธ๋ธ์ผ๋ ๋ธ ํ๊ต์ํ๋ณธ์ ",
"place_url": "http://place.map.kakao.com/1003710555",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก 145",
"x": "127.109194827252",
"y": "37.3942739207134"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋ฐฑํ๋ 535",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > ์ด๋งํธ24",
"distance": "158",
"id": "327879494",
"phone": "",
"place_name": "์ด๋งํธ24 Rํ๊ต์ํ๋",
"place_url": "http://place.map.kakao.com/327879494",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋ถ๋น๋ด๊ณก๋ก 117",
"x": "127.112130909636",
"y": "37.394199091245"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋ฐฑํ๋ 531",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > ์ธ๋ธ์ผ๋ ๋ธ",
"distance": "216",
"id": "1824252087",
"phone": "",
"place_name": "์ธ๋ธ์ผ๋ ๋ธ ํ๊ต์ํ์ ",
"place_url": "http://place.map.kakao.com/1824252087",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋์ํ๊ต๋ก606๋ฒ๊ธธ 10",
"x": "127.10903452806154",
"y": "37.395876085264874"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ์ผํ๋ 646",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ ",
"distance": "253",
"id": "18830408",
"phone": "031-8017-2851",
"place_name": "์์คํ ์ดํธ์์ ",
"place_url": "http://place.map.kakao.com/18830408",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก 178",
"x": "127.110042792158",
"y": "37.396493252041"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ์ผํ๋ 651",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ ",
"distance": "266",
"id": "21610813",
"phone": "031-8017-0662",
"place_name": "์ํ์ ํดํผํ๊ต์ญ์ ",
"place_url": "http://place.map.kakao.com/21610813",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋์ํ๊ต๋ก606๋ฒ๊ธธ 39",
"x": "127.11150407155293",
"y": "37.396436019780545"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ์ผํ๋ 647",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > CU",
"distance": "274",
"id": "19729729",
"phone": "031-8017-6771",
"place_name": "CU ํ๊ต์ญ์ ",
"place_url": "http://place.map.kakao.com/19729729",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋์ํ๊ต๋ก606๋ฒ๊ธธ 31",
"x": "127.11096907172671",
"y": "37.396643758006725"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ์ผํ๋ 741",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > CU",
"distance": "274",
"id": "26776001",
"phone": "031-702-0135",
"place_name": "CU ํ๊ต์๋๋งํฌ์ ",
"place_url": "http://place.map.kakao.com/26776001",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋์ํ๊ต๋ก606๋ฒ๊ธธ 58",
"x": "127.11329416621403",
"y": "37.394998089782916"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ์ผํ๋ 643",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > ์ด๋งํธ24",
"distance": "327",
"id": "1970035485",
"phone": "070-8801-5864",
"place_name": "์ด๋งํธ24 ํ๊ต์ญ์ ",
"place_url": "http://place.map.kakao.com/1970035485",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก 184",
"x": "127.11013412793073",
"y": "37.39716893309608"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋ฐฑํ๋ 529",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > GS25",
"distance": "358",
"id": "27498673",
"phone": "",
"place_name": "GS25 ํ๊ตํ๋ธ์ ",
"place_url": "http://place.map.kakao.com/27498673",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก 109",
"x": "127.109027707855",
"y": "37.3911673507558"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋ฐฑํ๋ 529",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > CU",
"distance": "358",
"id": "26776000",
"phone": "031-708-4755",
"place_name": "CU ํ๊ตSKํ๋ธ์ ",
"place_url": "http://place.map.kakao.com/26776000",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก 109",
"x": "127.109027707855",
"y": "37.3911673507558"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ์ผํ๋ 661",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > GS25",
"distance": "360",
"id": "2137856685",
"phone": "1644-5425",
"place_name": "GS25 ํ๊ต์๋ฏธ์ง์ ",
"place_url": "http://place.map.kakao.com/2137856685",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก192๋ฒ๊ธธ 14-1",
"x": "127.11173796948",
"y": "37.3972809570947"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ์ผํ๋ 655",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > ์ด๋งํธ24",
"distance": "361",
"id": "2141413165",
"phone": "031-708-8334",
"place_name": "์ด๋งํธ24 ํ๊ตSG์ ",
"place_url": "http://place.map.kakao.com/2141413165",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ๋์ํ๊ต๋ก606๋ฒ๊ธธ 47",
"x": "127.1123881246",
"y": "37.3970460753739"
},
{
"address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ์ผํ๋ 662",
"category_group_code": "CS2",
"category_group_name": "ํธ์์ ",
"category_name": "๊ฐ์ ,์ํ > ํธ์์ > GS25",
"distance": "407",
"id": "979082513",
"phone": "031-781-6910",
"place_name": "GS25 ํ๊ตํ์์ ",
"place_url": "http://place.map.kakao.com/979082513",
"road_address_name": "๊ฒฝ๊ธฐ ์ฑ๋จ์ ๋ถ๋น๊ตฌ ํ๊ต์ญ๋ก192๋ฒ๊ธธ 16",
"x": "127.11141235322867",
"y": "37.39779845058796"
}
]
JSON ์์ ๋ฐ์ดํฐ
'โ ๏ธ deprecated โ ๏ธ > ํจ์บ (์ด๊ฒฉ์ฐจ)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
part4 (ch1). MyAssets ์ฝ๋๋ฆฌ๋ทฐ(feat. SwiftUI) (0) | 2022.02.23 |
---|---|
part5 (ch1). FindCVS ์ฝ๋๋ฆฌ๋ทฐ (0) | 2022.02.20 |
part5 (ch6). KeywordNews XCTest ์ฝ๋๋ฆฌ๋ทฐ (0) | 2022.02.17 |
part5 (ch6). KeywordNews ์ฝ๋๋ฆฌ๋ทฐ (0) | 2022.02.15 |
part5 (ch6). ๐ช CI/CD๋? (feat. bitrise) (0) | 2022.02.15 |