58
Александр Сычев Ведущий разработчик iOS Тесты и Swift

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

Embed Size (px)

Citation preview

Александр Сычев Ведущий разработчик iOS

Тесты и Swift

Ретроспектива

Советы и рекомендации

Ретроспектива

Советы и рекомендации

xUnit

What makes a clean test?

Three things. Readability, readability, and readability.

Robert C. Martin, «Clean Code»

Предметно-ориентированный язык

Нет лишнего контекста

Тестируем одно поведение

52

Библиотеки

Ретроспектива

Советы и рекомендации

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() }}

Mocksfunc testExample() { // given

...

// when ...

// then XCTAssertEqual(mockURLSession.dataTaskLastURL?.host, "http://expected.com")}

Mocks

1. Используйте вспомогательные методы для одной проверки

2. Накапливайте аргументы в коллекциях

Partial mocks

Partial mocksclass MockURLSessionDataTask: URLSessionDataTask { private var resumeCallCount = 0

override func resume() { resumeCallCount += 1 }

func verifyResume() { XCTAssertEqual(resumeCallCount, 1) }}

Partial mocksclass 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

Да, есть библиотеки

1.Чистые тесты должны оставаться чистыми

2.Используйте коллекции вспомогательных методов

3.Добавляйте методы проверок к мокам

4.Используйте #file и #line

5.Не мокируйте value types

[email protected]

@asychev89