SlideShare a Scribd company logo
Александр Сычев
Ведущий разработчик iOS
Тесты и Swift
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
Ретроспектива
Советы и рекомендации
Ретроспектива
Советы и рекомендации
xUnit
RDSDataSource: Чистые тесты на Swift
What makes a clean test?
Three things. Readability, readability, and readability.
Robert C. Martin, «Clean Code»
Предметно-ориентированный язык
Нет лишнего контекста
Тестируем одно поведение
52
Библиотеки
Ретроспектива
Советы и рекомендации
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
Assertions
Assertions
public func XCTFail(_ message: String = default,
file: StaticString = #file,
line: UInt = #line)
Assertions
func testExample() {
// given
// when
// then
XCTFail("message")
}
Assertions
func testExample() {
// given
let pair = (20, 16)
// when
// then
XCTAssertEqual(pair, (20, 17))
}
Assertions
func testExample() {
// given
let pair = (20, 16)
// when
// then
XCTAssertEqual(pair, (20, 17))
}
Assertions
func testExample() {
// given
let pair = (20, 16)
// when
// then
XCTAssert(pair == (20, 17))
}
Assertions
Assertions
Write a custom test assertion
Custom assertions
func assertIntPairsEqual(actual: (_: Int, _: Int),
expected: (_: Int, _: Int),
file: StaticString = #file,
line: UInt = #line) {
if actual != expected {
XCTFail("Expected (expected) but was (actual)",
file: file, line: line)
}
}
Custom assertions
func assertIntPairsEqual(actual: (_: Int, _: Int),
expected: (_: Int, _: Int),
file: StaticString = #file,
line: UInt = #line) {
if actual != expected {
XCTFail("Expected (expected) but was (actual)",
file: file, line: line)
}
}
Custom assertions
func assertIntPairsEqual(actual: (_: Int, _: Int),
expected: (_: Int, _: Int),
file: StaticString = #file,
line: UInt = #line) {
if actual != expected {
XCTFail("Expected (expected) but was (actual)",
file: file, line: line)
}
}
Custom assertions
Custom assertions
func assertPairsEqual<T: Equatable, U: Equatable>(actual: (_: T, _: U),
expected: (_: T, _: U),
file: StaticString = #file,
line: UInt = #line)
Mocks
Mocks
1. Надежность
2. Скорость
Mocks
NSURLSession *mock = OCMClassMock([NSURLSession class]);
Mocks
NSURLSession *mock = OCMClassMock([NSURLSession class]);
Protocols and Extensions
Mocks
struct ToDoService {
init(session: URLSession) {
// …
}
}
let service = ToDoService(session: URLSession.shared)
Mocks
struct ToDoService {
init(session: URLSession) {
// …
}
}
let service = ToDoService(session: URLSession.shared)
Mocks
protocol URLSessionProtocol {}
extension URLSession: URLSessionProtocol {}
struct ToDoService {
init(session: URLSessionProtocol) {
// …
}
}
Mocks
protocol URLSessionProtocol {}
extension URLSession: URLSessionProtocol {}
struct ToDoService {
init(session: URLSessionProtocol) {
// …
}
}
Mocks
protocol URLSessionProtocol {}
extension URLSession: URLSessionProtocol {}
struct ToDoService {
init(session: URLSessionProtocol) {
// …
}
}
Mocks
protocol URLSessionProtocol {}
extension URLSession: URLSessionProtocol {}
struct ToDoService {
init(session: URLSessionProtocol) {
// …
}
}
Mocks
let service = ToDoService(session: URLSession.shared)
Mocks
class MockURLSession: URLSessionProtocol {}
Mocks
protocol URLSessionProtocol {
func dataTask(request: URLRequest,
completion:(Data?, Response?, Error?) -> Swift.Void) -> DataTask
}
Mocks
class MockURLSession: URLSessionProtocol {
func dataTask(with …) -> URLSessionDataTask {
return URLSessionDataTask()
}
}
Mocks
1. Число вызовов
2. Переданные аргументы
Mocks
class MockURLSession: URLSessionProtocol {
var dataTaskCallCount = 0
func dataTask(with …) -> URLSessionDataTask {
dataTaskCallCount += 1
return URLSessionDataTask()
}
}
Mocks
func testExample() {
// given
...
// when
...
// then
XCTAssertEqual(mockURLSession.dataTaskCallCount, 1)
}
Mocks
class MockURLSession: URLSessionProtocol {
var dataTaskLastURL: URL?
func dataTask(with …) -> URLSessionDataTask {
dataTaskLastURL = url
return URLSessionDataTask()
}
}
Mocks
func testExample() {
// given
...
// when
...
// then
XCTAssertEqual(mockURLSession.dataTaskLastURL?.host,
"http://expected.com")
}
Mocks
1. Используйте вспомогательные методы для одной проверки
2. Накапливайте аргументы в коллекциях
Partial mocks
Partial mocks
class MockURLSessionDataTask: URLSessionDataTask {
private var resumeCallCount = 0
override func resume() {
resumeCallCount += 1
}
func verifyResume() {
XCTAssertEqual(resumeCallCount, 1)
}
}
Partial mocks
class MockURLSession: URLSessionProtocol {
var dataTaskReturnValue: URLSessionDataTask!
func dataTask(with …) -> URLSessionDataTask {
return dataTaskReturnValue
}
}
Partial mocks
func testExample() {
// given
let mockDataTask = MockURLSessionDataTask()
mockURLSession.dataTaskReturnValue = mockDataTask
// when
…
// then
mockDataTask.verifyResume()
}
Partial mocks
💩
Выводы
1.Чистые тесты должны оставаться чистыми
2.Используйте коллекции вспомогательных методов
3.Добавляйте методы проверок к мокам
4.Используйте #file и #line
5.Не мокируйте value types
RDSDataSource: Чистые тесты на Swift
Да, есть библиотеки
1.Чистые тесты должны оставаться чистыми
2.Используйте коллекции вспомогательных методов
3.Добавляйте методы проверок к мокам
4.Используйте #file и #line
5.Не мокируйте value types
a.sychev@rambler-co.ru
@asychev89

More Related Content

RDSDataSource: Чистые тесты на Swift