
ํ๋ก์ ํธ์ ๊ท๋ชจ๊ฐ ๋น๋ํด์ง์๋ก ์ฝ๋ ๊ตฌ์กฐ๊ฐ ์ค์ํด์ง๊ณ ์ด์ ๋ฐ๋ผ ์์กด์ฑ ๊ด๋ฆฌ๋ฅผ ์ ํด์ฃผ์ด์ผ ํฉ๋๋ค. ์์กด์ฑ์ด ์ธ๋ถ์์ ๋ด๋ถ๋ก ํฅํ ์ ์๋๋ก ์ฒ ์ ํ ํด์ผํ๊ณ , ๋ชจ๋ ๊ฐ ์ธํฐํ์ด์ค๋ก ๊ฒฐํฉ์ด ๋๋ฏ๋ก ํ์ ์ ํ๊ฒฝ์ ๋ฐ๋ผ ์ธํฐํ์ด์ค์ ๊ตฌํ์ ๊ฐ์ ๋ผ์ธ ์ ์๋๋ก ํด์ผํฉ๋๋ค.
Tuist๋ก ํ๊ฒ ์์กด์ฑ ๊ด๋ฆฌํ๊ธฐTuist๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก์ ํธ ๋ชจ๋์ ์์กด์ฑ๊ณผ ์ธ๋ถ ์์กด์ฑ์ ๊ด๋ฆฌํ์์ต๋๋ค.
๊ธฐ์กด์๋ ์ฌ๋ฌ ์ฐจ๋ก ์ฌ์ฉ ๊ฒฝํ์ด ์์๊ณ ์ฝ๊ฒ ๋ชจ๋ํ๋ฅผ ์งํํ๊ณ ์ธ๋ถ ์์กด์ฑ์ ๊ด๋ฆฌํ ์ ์๊ธฐ ๋๋ฌธ์ Tuist๋ฅผ ์ฌ์ฉํ๊ฒ ๋์์ต๋๋ค.
์ด๋ฒ ๊ธฐํ์ ๊ธฐ์กด๊ณผ๋ ์๋ก์ด ๋ฐฉ๋ฒ์ ์๋ํ ๊ฒ์ ์๋์ ๊ฐ์ด ํ๋ก์ ํธ๋ฅผ ๊ตฌ์ฑํ ๋ ์ฑ ํ๊ฒ๊ณผ ํ ์คํธ ํ๊ฒ ์์กด์ฑ์ ๋ฐ๋ก ๋ฐ์ ํ๊ฒ์ ๋ง๊ฒ ํ๋ก์ ํธ๋ฅผ ๋น๋ํ์์ต๋๋ค.
๋ํ Project๋ฅผ ๊ตฌ์ฑํ ๋ App Target์ ์์กด์ฑ๋ค๊ณผ Test Target ์์กด์ฑ์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ๊ฐ Target์ ๋ง๊ฒ ํ๋ก์ ํธ๋ฅผ ์ค์ ํ์์ต๋๋ค.
let project = Project.makeModule(name: "Feature",
product: .staticFramework,
appTargetDependencies: [.SPM.RxCocoa,
.SPM.RxDataSources,
.SPM.RxGesture,
.SPM.RxSwift,
// ...
.Workspace.DesignSystem,
// ...
],
testTargetDependencies: [.Workspace.TestUtility]
)
์ฌ์ฌ์ฉ ๊ฐ๋ฅ์ฑ์ด ์๋ ํ ์คํธ์ ๊ด๋ จํ ๊ฐ์ฒด ๋ฐ ํจ์๋ค์ ๋ณ๋์ ๋ชจ๋๋ก ๋ถ๋ฆฌํ์๊ณ , ํ ์คํธ ํ๊ฒ์๋ง ์ด๋ฅผ ์์กดํ๋๋ก ํ์ฌ ์ ํํ๊ณ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์์์ต๋๋ค.
Swinject๋ก ๊ฐ์ฒด ๊ด๋ฆฌํ๊ธฐ๊ฐ์ฒด๋ค์ ์์กด์ฑ์ Swinject๋ฅผ ์ฌ์ฉํ์ฌ ๊ด๋ฆฌํ์์ต๋๋ค. Swinject์ ์์กด์ฑ ์ปจํ
์ด๋๋ฅผ ํ์ฉํด ๋ณด๋ค ๊ฐํธํ๊ฒ register, resolveํ์ฌ ์ฌ์ฉํ ์ ์๊ณ , ์์กด์ฑ ๋ฑ๋ก ์ ๊ฐ์ฒด์ ์๋ช
์ฃผ๊ธฐ์ ๊ตฌํ ํ๋กํ ์ฝ ๋ฑ์ ์ค์ ํ ์ ์์๊ธฐ ๋๋ฌธ์ ๋ค์ํ ๋ ์ด์ด๋ก ๋๋๊ณ ์์ชฝ์ ๋ฐ๋ผ๋ณด๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ ํ๋ก์ ํธ์์ ์ฌ์ฉํ๊ธฐ ์ ํฉํ ๊ฒ์ด๋ผ๊ณ ํ๋จํ์ต๋๋ค.
Feature๋ชจ๋์ ViewController, ViewModel๋ฑ์ ๊ฐ์ฒด๋ค๊ณผ
Core, Network๋ฑ์ ๋ชจ๋์ UseCase, Repository ๊ตฌํ ๊ฐ์ฒด๋ฑ์ ์์กด์ฑ์ ๋ชจ๋ Swinject๋ฅผ ํตํด ๊ด๋ฆฌํ์์ต๋๋ค.
MockingAssembly๋ฅผ ์ฌ์ฉํ ๋ ์๋์ ๊ฐ์ด MockableAssembly๋ฅผ ํตํด ํ์ ์ Mock ๊ฐ์ฒด ์์กด์ฑ๋ ํจ๊ป ๋ฑ๋กํ์์ต๋๋ค.
import Swinject
public protocol MockableAssembly: Assembly {
func assembleActual(_ container: Container)
func assembleMock(_ container: Container)
}
extension MockableAssembly {
public func assemble(container: Container) {
self.assembleActual(container)
self.assembleMock(container)
}
}
import Swinject
struct MyAssembly: MockableAssembly {
func assembleActual(_ container: Container) {
container.register(MyRepository.self) { _ in return MyRepositoryImpl() }
container.register(MyUseCase.self) { resolver in
let aService = resolver.resolve(AService.self)!
let bService = resolver.resolve(BService.self)!
return MyUseCaseImpl(aService: aService, bService: bService)
}
}
func assembleMock(_ container: Container) {
container.register(MyRepository.self, name: "Mock") { _ in return MockMyRepositoryImpl() }
container.register(MyUseCase.self, name: "Mock") { resolver in
let aService = resolver.resolve(AService.self, name: "Mock)!
let bService = resolver.resolve(BService.self, name: "Mock)!
return MyUseCaseImpl(aService: aService, bService: bService)
}
}
}
๊ทธ๋ฆฌ๊ณ ์ ๋ ํ ์คํธ๋ฅผ ์งํํ ๋๋ ๋น๋ ์คํค๋ง์์๋ ํธ๋ฆฌํ๊ฒ Mock ๊ฐ์ฒด๋ฅผ resolveํ ์ ์์์ต๋๋ค.
#if DEBUG
let viewModel = container.resolve(MyViewModel.self, name: "Mock")!
#else
let viewModel = container.resolve(MyViewModel.self)!
#endif
MockableAssembly์ ๋ณ๋ก์ธ ๋ฏMockableAssembly๋ฅผ ํตํด ์ค์ ๊ตฌํ ๊ฐ์ฒด์ Mock ๊ฐ์ฒด๋ฅผ ํ๊ฒฝ์ ๋ฐ๋ผ ์ฌ์ฉํ๋๋ฐ, ๊ตฌํ ๊ฐ์ฒด์ Mock ๊ฐ์ฒด๊ฐ ๋์์ ํ์ํ ๊ฒฝ์ฐ๋ ์์์ต๋๋ค.
๋ฐ๋ผ์ ๊ตณ์ด MockableAssembly ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ง ์๊ณ , assemble(container: ) ๋ฉ์๋์์ ํ์ ์ ์ง์ ์คํค๋ง ์กฐ๊ฑด ๋ถ๊ธฐ๋ฅผ ํตํด Mock ๊ฐ์ฒด๋ฅผ ๋ฑ๋กํ๊ณ ์ด๋ฅผ ๋ฐ๋ก ์ฌ์ฉํ๋ ํธ์ด ๋ ๋ซ๋ค๊ณ ์๊ฐ๋ฉ๋๋ค.