Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/leoture/MockSwift

MockSwift is a Mock library written in Swift.
https://github.com/leoture/MockSwift

mock playground swift swift-package-manager test xcode

Last synced: about 2 months ago
JSON representation

MockSwift is a Mock library written in Swift.

Awesome Lists containing this project

README

        


MockSwift

Welcome to MockSwift
=======

[![Release](https://img.shields.io/github/v/release/leoture/MockSwift?color=red)](https://github.com/leoture/MockSwift/releases)
[![Build Status](https://github.com/leoture/MockSwift/workflows/Master/badge.svg?branch=master)](https://github.com/leoture/MockSwift/actions)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=leoture_MockSwift&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=leoture_MockSwift)
[![Netlify Status](https://api.netlify.com/api/v1/badges/08a89377-0a44-4f0e-a17c-9ee7d18e07bd/deploy-status)](https://app.netlify.com/sites/adoring-varahamihira-bbe99d/deploys)
[![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-Compatible-brightgreen)](https://github.com/apple/swift-package-manager)
[![Swift](https://img.shields.io/badge/Swift-5.5-important)](https://swift.org)
[![license MIT](https://img.shields.io/badge/license-MIT-informational)](https://github.com/leoture/MockSwift/blob/master/LICENSE)

**MockSwift** allows you to [**write mocks**](#write-mocks) and [**make better tests**](#make-better-tests). Because **MockSwift** is an **open source** library **100%** written in **Swift**, it is **AVAILABLE ON ALL PLATFORMS**.
Initially MockSwift is inspired by [Mockito](https://site.mockito.org).

###### Table of Contents

- [Installation](#installation)
- [Usage](#usage)
- [Documentation](#documentation)
- [Contribution](#contribution)
- [License](#license)

# Installation
## Swift Package Manager
MockSwift has been designed to work with [Swift Package Manager](https://swift.org/package-manager/).
```swift
// swift-tools-version:5.3

import PackageDescription

let package = Package(
name: "MyProject",
dependencies: [
.package(url: "https://github.com/leoture/MockSwift.git", from: "1.0.0")
],
targets: [
.testTarget(name: "MyProjectTests", dependencies: ["MockSwift"])
]
)
```

# Usage
## Quick Look
```swift
class AwesomeTests: XCTestCase {

private var printer: Printer!
@Mock private var userService: UserService

override func setUp() {
printer = Printer(userService)
}

func test_sayHello() {
// Given
given(userService).fetchUserName(of: "you").willReturn("my friend")
given(userService).isConnected.get.willReturn(true)
given(userService)[cache: .any()].set(.any()).willDoNothing()

// When
let message = printer.sayHello(to: "you", from: "me")

// Then
then(userService).fetchUserName(of: .any()).called()
then(userService).isConnected.get.called(times: 1)
then(userService)[cache: "you"].set("my friend").calledOnce()

XCTAssertEqual(message, "me: Hello my friend")
}
}
```

## Details
Suppose that you have a `UserService` protocol.
```swift
struct User: Equatable {
let identifier: String
let name: String
}

protocol UserService {
func fetch(identifier: String) -> User
}
```

And you want to test this `UserCore` class.
```swift
class UserCore {
private let service: UserService

init(_ service: UserService) {
self.service = service
}

func fetchCurrentUser() -> User {
service.fetch(identifier: "current")
}
}
```

## Make better tests

Now, with MockSwift, you can use a mocked `UserService` in your tests with the `@Mock` annotation.

```swift
@Mock private var service: UserService

// equivalent to

private var service: UserService = Mock()
```

And easly configure it to fully test `UseCore`.

```swift
class UserCoreTests: XCTestCase {

private var core: UserCore!
@Mock private var service: UserService

override func setUp() {
core = UserCore(service)
}

func test_fetchCurrentUser() {
// Given
let expectedUser = User(identifier: "current", name: "John")

given(service).fetch(identifier: .any()).willReturn(expectedUser)

// When
let user = core.fetchCurrentUser()

// Then
then(service).fetch(identifier: .any()).called()
XCTAssertEqual(user, expectedUser)
}
}
```

### Given
`given()` enables you to define behaviours.
example:
```swift
given(service).fetch(identifier: .any()).willReturn(expectedUser)

// equivalent to

given(service) {
$0.fetch(identifier: .any()).willReturn(expectedUser)
}
```

```swift
given(service) {
$0.fetch(identifier: "current")
.willReturn(expectedUser, expectedUser1, expectedUser2)

$0.fetch(identifier: .match(when: \.isEmpty))
.will { (params) -> User in
// do something else
return expectedUser
}
}
```

you can also define behaviours when you instantiate the mock.

```swift
@Mock({
$0.fetch(identifier: .any()).willReturn(expectedUser)
})
private var service: UserService
```

### Then
`then()` enables you to verify calls.
example:

```swift
then(service).fetch(identifier: .any()).called()

// equivalent to

then(service) {
$0.fetch(identifier: .any()).called()
}
```

```swift
then(service) {
$0.fetch(identifier: "current").called(times: >=2)

$0.fetch(identifier: == "").called(times: 0)
}
```

You can go further and verify order of calls
```swift
let assertion = then(service).fetch(identifier: "current").called(times: >=2)
then(service).fetch(identifier: == "").called(times: 1, after: assertion)
```

### Stubs

In MockSwift, stubs are default values that are returned when no behaviours has been found.

#### Global Stubs

You can define a **global stub** for any type.
It will concern **all mocks** you will use in **every tests**.

```swift
extension User: GlobalStub {
static func stub() -> User {
User(identifier: "id", name: "John")
}
}
```

#### Local Stubs
You can also define a **stub localy** for any type.
It will concern only the **current mock**.
```swift
@Mock(localStubs: [
User.self => User(identifier: "id", name: "John")
])
private var service: UserService
```

### Strategy
The default strategy is to find behaviour defined with `given()`. If no behaviour is found, it will return a local stub. If no local stub is found, it will return a global stub.

```swift
@Mock private var service: UserService

// equivalent to

@Mock(strategy: .default)
private var service: UserService

// equivalent to

@Mock(strategy: [.given, .localStubs, .globalStubs])
private var service: UserService
```

You can change the order of the strategy list or remove items as you want.

## Write mocks
### Automatically
MockSwift provides a [stencil template](https://github.com/leoture/MockSwift/blob/master/Templates/MockSwift.stencil) for [sourcery](https://github.com/krzysztofzablocki/Sourcery). You can use the `AutoMockable` annotation to generate code.
```swift
// sourcery: AutoMockable
protocol UserService {
func fetch(identifier: String) -> User
}
```

To generate code at every build, you can add a build phase before `Compile Sources`.
```
sourcery \
--sources MyLibrary \
--templates MyLibraryTests/path/to/MockSwift.stencil \
--output MyLibraryTests/path/to/GeneratedMocks.swift \
--args module=MyLibrary
```

### Manually
To enable MockSwift for UserService type, you have to extend **Mock**.
```swift
extension Mock: UserService where WrappedType == UserService {
public func fetch(identifier: String) -> User {
mocked(identifier)
}
}
```

To allow behaviour definition through `given()` method, you have to extend **Given**.
```swift
extension Given where WrappedType == UserService {
public func fetch(identifier: Predicate) -> Mockable {
mockable(identifier)
}
public func fetch(identifier: String) -> Mockable {
mockable(identifier)
}
}
```

To allow call verification through `then()` method, you have to extend **Then**.
```swift
extension Then where WrappedType == UserService {
public func fetch(identifier: Predicate) -> Verifiable {
verifiable(identifier)
}
public func fetch(identifier: String) -> Verifiable {
verifiable(identifier)
}
}
```

# Documentation
If you need more details about the API, you can check out our [API documentation](https://adoring-varahamihira-bbe99d.netlify.app).

# Contribution
Would you like to contribute to MockSwift? Please read our [contributing guidelines](https://github.com/leoture/MockSwift/blob/master/CONTRIBUTING.md) and [code of conduct](https://github.com/leoture/MockSwift/blob/master/CODE_OF_CONDUCT.md).

# License
MockSwift is released under the MIT license. [See LICENSE](https://github.com/leoture/MockSwift/blob/master/LICENSE) for details.

# Credits
[![Thanks to JetBrains](https://raw.githubusercontent.com/leoture/MockSwift/master/jetbrains.svg?sanitize=true)](https://www.jetbrains.com/?from=MockSwift)