Ecosyste.ms: Awesome

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

https://github.com/sabirvirtuoso/Mockit

A simple mocking framework for Swift, inspired by the famous http://mockito.org/
https://github.com/sabirvirtuoso/Mockit

mocking-framework mockit swift

Last synced: about 1 month ago
JSON representation

A simple mocking framework for Swift, inspired by the famous http://mockito.org/

Lists

README

        

# Mockit

[![Build Status](https://travis-ci.org/sabirvirtuoso/Mockit.svg?branch=master)](https://travis-ci.org/sabirvirtuoso/Mockit)
[![Coverage Status](https://coveralls.io/repos/github/sabirvirtuoso/Mockit/badge.svg?branch=master)](https://coveralls.io/github/sabirvirtuoso/Mockit?branch=master)
[![Code Climate](https://codeclimate.com/github/sabirvirtuoso/Mockit/badges/gpa.svg)](https://codeclimate.com/github/sabirvirtuoso/Mockit)
[![Version](https://img.shields.io/cocoapods/v/Mockit.svg?style=flat)](http://cocoapods.org/pods/Mockit)
[![License](https://img.shields.io/cocoapods/l/Mockit.svg?style=flat)](http://cocoapods.org/pods/Mockit)
[![Platform](https://img.shields.io/cocoapods/p/Mockit.svg?style=flat)](http://cocoapods.org/pods/Mockit)
[![Swift Version](https://img.shields.io/badge/Swift-5.0-F16D39.svg?style=flat)](https://developer.apple.com/swift)

![Mockit GIF](Mockit.png)

## Introduction

`Mockit` is a **Tasty mocking framework for unit tests in Swift 5.0**. It's at an early stage of development, but its current features are almost completely usable.

`Mockit` is a mocking framework that tastes brilliant. It lets you write beautiful tests with a clean & simple API. Tests written using `Mockit` are very readable and they produce clean verification errors. It's inspired by the famous mocking framework for Java - [Mockito](http://mockito.org/).

## Documentation

`Mockit` is yet to be documented fully but it comes with a sample project that lets you try all its features and become familiar with the API. You can find it in `Mockit.xcworkspace`.

There's an example test file called ```ExampleTests.swift```. Look there for some tests that can be run. This tests a class ```Example``` against a mocked collaborator ```ExampleCollaborator```.

File an issue if you have any question.

To run the example project, clone the repo, and run `pod install` from the Example directory first.

## Limitations

* There's some boiler-plate code needed to create mocks. See ```MockExampleCollaborator``` for an example in the *Basic Usage* section below. However, a plugin development is on its way for both `Xcode` and `AppCode` to minimize writing this boiler-plate code every time a mock needs to be created.

## Features

- **Stubbing**. *Mockit* lets you stub a method and then perform any of 3 actions (`thenReturn`, `thenDo`, `thenAnswer`) individually or in any order via chaining;
- **Mocking**. You can create a subclass extending the ```Mock``` protocol to mock required methods;
- **Call Verification**. You can verify method calls using one of 8 supported modes (`Once`, `AtLeastOnce`, `AtMostOnce`, `Times`, `AtLeastTimes`, `AtMostTimes`, `Never` and `Only`);
- **Arguments of specific call**. *Mockit* allows you to obtain the arguments of a specific method call to use custom assertions on them;
- **Helpful messages**. If method verification fails or something goes wrong, Mockit provides readable messages that describes the issue;
- **Default Type matchers**. Out of the box, *Mockit* can match the following types:

* String / String?
* Bool / Bool?
* Int / Int?
* Double / Double?
* Float / Float?
* Array / Array? of the above primitive types
* Dictionary / Dictionary? of the above primitive types

Given that Swift does not have reflection, *Mockit* cannot magically match your custom types, so you need to subclass ```TypeMatcher``` protocol to write
your one custom type matcher. For an example, see the *Basic Usage* section below.

## Basic Usage

The examples below assume we are mocking this class:

```swift
class ExampleCollaborator {

func voidFunction() {
}

func function(int: Int, _ string: String) -> String {
return ""
}

func stringDictFunction(dict: [String: String]) -> String {
return ""
}

}
```

In your test code, you'll need to create a ```MockExampleCollaborator```, which extends ```ExampleCollaborator``` and adopts ```Mock```. The mock creates a ```CallHandler```, and forwards all calls to it:

```swift
class MockExampleCollaborator: ExampleCollaborator, Mock {

let callHandler: CallHandler

init(testCase: XCTestCase) {
callHandler = CallHandlerImpl(withTestCase: testCase)
}

func instanceType() -> MockExampleCollaborator {
return self
}

override func voidFunction() {
callHandler.accept(nil, ofFunction: #function, atFile: #file, inLine: #line, withArgs: nil)
}

override func function(int: Int, _ string: String) -> String {
return callHandler.accept("", ofFunction: #function, atFile: #file, inLine: #line, withArgs: int, string) as! String
}

override func stringDictFunction(dict: Dictionary) -> String {
return callHandler.accept("", ofFunction: #function, atFile: #file, inLine: #line, withArgs: dict) as! String
}

}
```

To write a custom type matcher:

```swift
public class CustomMatcher: TypeMatcher {

public func match(argument arg: Any, withArgument withArg: Any) -> Bool {
switch (arg, withArg) {
case ( _ as CustomType, _ as CustomType):
// custom matching code here
return true
default:
return false
}
}

}
```

It is good practice to put the mock objects and custom type matchers in a separate group in the test part of your project.

### Currently-supported syntax

```swift
// stub a call on a method with parameters, then return value
mockCollaborator.when().call(withReturnValue: mockCollaborator.function(42, "frood")).thenReturn("hoopy")
```

```swift
// stub a call on a method with dictionary parameter, then answer value
mockCollaborator.when().call(withReturnValue: mockCollaborator.stringDictFunction(["Hello": "Pong"])).thenAnswer {
(args: [Any?]) -> String in

// custom code here
}
```

```swift
// stub a call on a void method , then do action
mockCollaborator.when().call(withReturnValue: mockCollaborator.voidFunction()).thenDo {
(args: [Any?]) -> Void in

// if the call is received, this closure will be executed
print("===== thenDo closure called =====")
}
```

```swift
// stub a call on a method , then return values on multiple calls
mockCollaborator.when().call(withReturnValue: mockCollaborator.stringDictFunction(["Hello": "Pong"])).thenReturn("ping", "hoopy")
```

```swift
// stub a call on a method , then chain multiple actions for corresponding calls
mockCollaborator.when().call(withReturnValue: mockCollaborator.stringDictFunction(["Hello": "Pong"])).thenReturn("ping", "hoopy").thenAnswer {
(args: [Any?]) -> String in

// custom code here
}
```

```swift
// call a method and then get arguments of a specific call which can be asserted later
systemUnderTest.doSomethingWithParamters(42, "frood")
systemUnderTest.doSomethingWithParamters(18, "hoopy")

let argumentsOfFirstCall = mockCollaborator.getArgs(callOrder: 1).of(mockCollaborator.function(AnyValue.int, AnyValue.string))
let argumentsOfSecondCall = mockCollaborator.getArgs(callOrder: 2).of(mockCollaborator.function(AnyValue.int, AnyValue.string))
```

With nice mocks, Mockit supports the **"verify expectations after mocking"** style using **8 supported verification modes**

```swift
// call method on the system under test
systemUnderTest.expectMethodOneAndThree()

// Then
mockCollaborator.verify(verificationMode: Once()).methodOne()
mockCollaborator.verify(verificationMode: Never()).methodTwo()
mockCollaborator.verify(verificationMode: Once()).methodThree()

// call method on the system under test
sut.expectMethodOneTwice()

// Then
mockCollaborator.verify(verificationMode: Times(times: 2)).methodOne()

// call method on the system under test
sut.expectOnlyMethodThree()

// Then
mockCollaborator.verify(verificationMode: Only()).methodThree()

// call method on system under test
sut.expectAllThreeMethods()

// Then
mockCollaborator.verify(verificationMode: Once()).methodOne()
mockCollaborator.verify(verificationMode: AtLeastOnce()).methodTwo()
mockCollaborator.verify(verificationMode: AtMostOnce()).methodThree()

// call method on the system under test
sut.expectMethodTwoAndThree()

// Then
mockCollaborator.verify(verificationMode: AtLeastTimes(times: Times(times: 1))).methodTwo()
mockCollaborator.verify(verificationMode: Never()).methodOne()
mockCollaborator.verify(verificationMode: AtMostTimes(times: Times(times: 3))).methodThree()
```
## Requirements

* Xcode 9+
* XCTest

## Installation

Mockit is built with Swift 5.0.

#### CocoaPods

Mockit is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:

```ruby
pod 'Mockit', '1.5.0'
```

#### Manually
1. Download and drop ```/Mockit``` folder in your project.
2. Congratulations!

## Feedback

Issues and pull-requests are most welcome - especially about improving the API further.

## Author

Syed Sabir Salman-Al-Musawi, [email protected]

I'd also like to thank [**Sharafat Ibn Mollah Mosharraf**]([email protected]) for his big support during the API design and development phase.

## License

**Mockit** is available under the **MIT license**. See the `LICENSE` file for more info.

The PNG at the top of this `README.md` is from [www.mockit.co.uk/about.html](http://www.mockit.co.uk/about.html)