mirror of
https://github.com/mas-cli/mas
synced 2024-11-23 03:53:09 +00:00
129 lines
5.3 KiB
Markdown
129 lines
5.3 KiB
Markdown
# Mocks 테스트 (모의 테스트)
|
|
|
|
## Test doubles
|
|
|
|
객체 간 종속성으로 인해 테스트를 작성할 때 문제가 발생할 수 있습니다. 예를 들어, `Tire`에 의존하거나 사용하는 `Car` 클래스가 있다고 가정해봅시다.
|
|
|
|
![](https://github.com/Quick/Assets/blob/master/Screenshots/TestUsingMock_BusesA.png)
|
|
|
|
`CarTests`는 `Tire`라고 불리는 `Car`를 테스트합니다. 이제 `Tire`의 버그로 인해 `CarTests`가 실패할 수 있습니다. (`Car`는 괜찮습니다) 이는 "무엇이 망가졌는가?" 라는 질문에 대답하기 어려울 수 있습니다.
|
|
|
|
이러한 문제를 피하려면, `CarTests`의 `Tire` 용 stand-in 객체를 사용할 수 있습니다. 이런 경우, `PerfectTire`라는 `Tire` 용 stand-in 객체를 만듭니다.
|
|
|
|
![](https://github.com/Quick/Assets/blob/master/Screenshots/TestUsingMock_BusesAmock.png)
|
|
|
|
`PerfectTire` 는 `Tire` 와 모두 동일한 public 함수와 프로퍼티를 가질 겁니다. 하지만, 이러한 일부 또는 전체의 함수 또는 프로퍼티에 대한 구현은 다를 수 있습니다.
|
|
|
|
`PerfectTire` 와 같은 객체는 "test doubles"로 불립니다. Test doubles는 관련 객체의 기능을 독립적으로 테스트하기 위한 "stand-in objects"로 사용됩니다. 여기 몇 가지 종류의 test doubles가 있습니다:
|
|
|
|
- Mock 객체: 테스트 객체에서 결과물을 받기 위해 사용됩니다.
|
|
- Stub 객체:테스트 객체의 입력을 제공하는 데 사용됩니다.
|
|
- Fake 객체: 원래 클래스와 비슷하지만, 단순화된 방식으로 작동합니다.
|
|
|
|
Mock 객체의 사용법부터 알아봅시다.
|
|
|
|
## Mock
|
|
|
|
Mock 객체는 다른 객체와의 정확한 상호 작용을 열거하고, 무언가가 잘못되었을 때를 감지하는 데 초점을 둡니다. Mock 객체는 테스트가 진행되는 도중에 호출되어야 하는 메소드와 mock 객체가 반환해야 하는 값을 (미리) 알아야 합니다.
|
|
|
|
Mock 객체는 다음과 같은 이유로 훌륭합니다:
|
|
|
|
- 훨씬 더 빨리 테스트합니다.
|
|
- 인터넷에 연결되어 있지 않은 경우에도 테스트를 실행합니다.
|
|
- 클래스를 종속성과 분리하여 테스트하는 데 중점을 둡니다.
|
|
|
|
### Swift에서 Mock 객체로 테스트 작성하기
|
|
|
|
#### Sample app
|
|
|
|
예를 들어, 인터넷에서 데이터를 검색하는 앱을 만들어 봅시다:
|
|
|
|
* 인터넷의 데이터는 `ViewController`에 표시되어야 합니다.
|
|
* 사용자 정의 클래스는 데이터를 가져오는 메서드를 지정하는 `DataProviderProtocol`을 상속합니다.
|
|
|
|
`DataProviderProtocol` 은 다음과 같이 정의됩니다:
|
|
|
|
```swift
|
|
protocol DataProviderProtocol: class {
|
|
func fetch(callback: (data: String) -> Void)
|
|
}
|
|
```
|
|
|
|
`fetch()` 는 인터넷에서 데이터를 가져와 `callback` 클로저를 사용하여 데이터를 반환합니다.
|
|
|
|
다음은, `DataProviderProtocol` 프로토콜을 따르는 `DataProvider`클래스 입니다.
|
|
|
|
```swift
|
|
class DataProvider: NSObject, DataProviderProtocol {
|
|
func fetch(callback: (data: String) -> Void) {
|
|
let url = URL(string: "http://example.com/")!
|
|
let session = URLSession(configuration: .default)
|
|
let task = session.dataTask(with: url) {
|
|
(data, resp, err) in
|
|
let string = String(data: data!, encoding: .utf8)
|
|
callback(data: string)
|
|
}
|
|
task.resume()
|
|
}
|
|
}
|
|
```
|
|
|
|
이 시나리오에서, `fetch()` 는 `ViewController` 의 `viewDidLoad()` 메소드에서 호출됩니다.
|
|
|
|
```swift
|
|
class ViewController: UIViewController {
|
|
|
|
// MARK: Properties
|
|
@IBOutlet weak var resultLabel: UILabel!
|
|
private var dataProvider: DataProviderProtocol?
|
|
|
|
// MARK: View Controller Lifecycle
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
dataProvider = dataProvider ?? DataProvider()
|
|
|
|
dataProvider?.fetch({ [unowned self] (data) -> Void in
|
|
self.resultLabel.text = data
|
|
})
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `DataProviderProtocol`의 Mock을 사용하여 테스트
|
|
|
|
`ViewController` 는 `DataProviderProtocol`을 신뢰합니다. 뷰 컨트롤러를 독립적으로 테스트하려면, `DataProviderProtocol` 을 따르는 mock 객체를 만들 수 있습니다.
|
|
|
|
```swift
|
|
class MockDataProvider: NSObject, DataProviderProtocol {
|
|
var fetchCalled = false
|
|
func fetch(callback: (data: String) -> Void) {
|
|
fetchCalled = true
|
|
callback(data: "foobar")
|
|
}
|
|
}
|
|
```
|
|
|
|
`fetch()` 가 호출되면, `fetchCalled` 프로퍼티가 `true`로 설정되어, 테스트에서 호출되었음을 확인할 수 있습니다.
|
|
|
|
다음 테스트에서는 `ViewController` 가 로드될 때, `dataProvider.fetch() `를 호출하는지 확인합니다.
|
|
|
|
```swift
|
|
override func spec() {
|
|
describe("view controller") {
|
|
it("fetch data with data provider") {
|
|
let mockProvider = MockDataProvider()
|
|
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController") as! ViewController
|
|
viewController.dataProvider = mockProvider
|
|
|
|
expect(mockProvider.fetchCalled).to(beFalse())
|
|
|
|
let _ = viewController.view
|
|
|
|
expect(mockProvider.fetchCalled).to(beTrue())
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
테스트를 작성하는 것에 관해 관심이 있어서 더 배우고 싶다면, 다음을 참조하세요. <https://realm.io/news/testing-in-swift/>.
|