Mockit is a Tasty mocking framework for unit tests in Swift 5.0

Overview

Mockit

Build Status Coverage Status Code Climate Version License Platform Swift Version

Mockit GIF

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.

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:

String { return "" } } ">
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:

) -> String { return callHandler.accept("", ofFunction: #function, atFile: #file, inLine: #line, withArgs: dict) as! String } } ">
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, String>) -> String {
      return callHandler.accept("", ofFunction: #function, atFile: #file, inLine: #line, withArgs: dict) as! String
    }

}

To write a custom type matcher:

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

// stub a call on a method with parameters, then return value
mockCollaborator.when().call(withReturnValue: mockCollaborator.function(42, "frood")).thenReturn("hoopy")
String in // custom code here } ">
// 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
    }
// 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 =====")
    }
// stub a call on a method , then return values on multiple calls
mockCollaborator.when().call(withReturnValue: mockCollaborator.stringDictFunction(["Hello": "Pong"])).thenReturn("ping", "hoopy")
String in // custom code here } ">
// 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
    }
// 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

// 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. To install it, simply add the following line to your Podfile:

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

Comments
  • Swift 5 migration and corresponding pod and READ me update

    Swift 5 migration and corresponding pod and READ me update

    • Updated Swift version to 5.0
    • Applied Swift 5.0 syntax
    • Pods installed to reflect Swift 5.0 update.
    • Incremented version to 1.5.0
    • Updated README to point to 1.5.0
    opened by VinutaPrabhu 3
  • Issue with testing func with a closure parameter

    Issue with testing func with a closure parameter

    Greetings! Have a some problems with testing func, which have call function with a closure parameter inside MVP architecture, testing non-mock Presenter with mocked ViewController Presenter - func chooseTypeOfPurpose() { view.showCategory(purposes: purposes, currentPurpose: currentPurpose) { (otherCategory) in if let category = otherCategory { self.setCategory(category) self.updateButtons() } } } MockViewController - func showCategory(purposes: [PurposeObject], currentPurpose: PurposeObject?, completion: @escaping (PurposeObject?) -> Void) { let viewController = PurposeViewController() viewController.purposeHandler = completion viewController.allPurposes = purposes viewController.currentPurpose = currentPurpose; self.navigationController?.pushViewController(viewController, animated: true) } So, what i can do in this situation? Can i handle completion with Mockit like in Mockito with argumentcaptor?

    opened by SheveleR 1
  • Add details to argument mismatch error

    Add details to argument mismatch error

    We experienced a strange error that caused some of our tests to crash, but only in our CI system, never locally. It was really hard to identify the issue, since the error message was not informative enough. This commit is supposed to give more detail about the actual stub when this argument mismatch error occurs.

    opened by andraskadar 0
  • Add Manual Installation

    Add Manual Installation

    CocoaPods is an awesome tool and it makes our life really easier, but there are some devs who still don't know how to use them.

    It would be cool to add the Manual installation guide in your README.md. You can take a look at my iOS Readme Template to see how you can do it.

    opened by lfarah 0
  • OSX Compatibility

    OSX Compatibility

    Presumably nothing in this framework actually depends on iOS - could you possibly mark the podspec as being compatible with OSX too? pod refuses to install it simply because I'm targeting OSX :)

    opened by arrtchiu 0
  • 1.5.0 version unavailable on CocoaPods

    1.5.0 version unavailable on CocoaPods

    Hello :)

    I recently tried to install Mockit via CocoaPods with the following line :

    pod 'Mockit', '1.5.0'

    Unfortunately, I get the : None of your spec sources contain a spec satisfying the dependency: Mockit (= 1.5.0) error.

    When I go to cocoa pods, the latest version is the 1.4.0 with a compilation error on MockMatcher line 33 (use public instead of open).

    I will go with manual installation for now but it would be great if the latest version of the library could be available on Cocoapods for CI :)

    Thanks in advance !

    opened by GhaisB 1
  • How to stub the same method twice with different arguments

    How to stub the same method twice with different arguments

    Hi, I need to stub the same method of my mockObject twice, but I can't use the variadic thenReturn because it's a generic method returning different types Single<T.Data> according to the T provided as an argument. (In the code above a Query is a protocol, and the method getSingle<T>(query: T) -> Single<T.Data> where T : Query accepts any class that implements query and returns a Single of type T.Data)

    let _ = self.mockDataSource.when().call(withReturnValue: self.mockDataSource.getSingle(query: QueryA())) .thenReturn(Single.just(payloadA))

    let _ = self.mockDataSource .when().call(withReturnValue: self.mockDataSource.getSingle(query: QueryB())) .thenReturn(Single.just(payloadB))

    However, the second stub is never called, always the first one, and my tests fail. I'd like to have the ability to create a stub returning a specific type according to the type provided as an argument, either by matching the provided type (I failed to do it with ArgumentMatcher) either by specifying the order...

    Thank you for your help,

    opened by matvdg 2
Owner
Syed Sabir Salman-Al-Musawi
Syed Sabir Salman-Al-Musawi
Freezer is a library that allows your Swift tests to travel through time by mocking NSDate class.

Freezer Freezer is a library that allows your Swift tests to travel through time by mocking NSDate class. Usage Once Freezer.start() has been invoked,

Sergey Petrov 8 Sep 24, 2022
Trying to implement Unit Tests for @Binding properties in a ViewModel

BindingTester Trying to implement Unit Tests for @Binding properties in a ViewModel ViewModel to be tested class SheetViewModel: ObservableObject {

Raphael Guye 0 Oct 22, 2021
Catching fatal errors in unit tests

Precondition Catching When running tests which hit fatal errors, often preconditions the built-in support with XCTest. One package which supports cach

Brennan Stehling 0 Nov 28, 2021
Write unit tests which test the layout of a view in multiple configurations

Overview This library enables you to write unit tests which test the layout of a view in multiple configurations. It tests the view with different dat

LinkedIn 565 Nov 16, 2022
Snapshot view unit tests for iOS

iOSSnapshotTestCase (previously FBSnapshotTestCase) What it does A "snapshot test case" takes a configured UIView or CALayer and uses the necessary UI

Uber Open Source 1.7k Jan 4, 2023
TestSchedulerDemo - Demonstration code for iOS Unit Tests and Asynchronous

TestSchedulerDemo This repository contains demonstration code for my Medium arti

Carsten Wenderdel 0 Mar 19, 2022
Marvel - Marvel Characters App using MVVM, and including unit tests

Marvel About The purpose of this project is to develop a app using MVVM, and inc

null 1 Mar 20, 2022
Detailed explanations and implementations of various maths concepts for writing high performance code/algorithms backed with Unit tests.

Detailed explanations and implementations of various maths concepts which can help software Engineers write high performance code/algorithms backed with Unit tests.

Mussa Charles 2 Sep 25, 2022
Boilerplate-free mocking framework for Swift!

Cuckoo Mock your Swift objects! Introduction Cuckoo was created due to lack of a proper Swift mocking framework. We built the DSL to be very similar t

Brightify 1.5k Dec 27, 2022
A convenient mocking framework for Swift

// Mocking let bird = mock(Bird.self) // Stubbing given(bird.getName()).willReturn("Ryan") // Verification verify(bird.fly()).wasCalled() What is Mo

Bird Rides, Inc 545 Jan 5, 2023
A mocking framework for Swift

SwiftMock SwiftMock is a mocking framework for Swift 5.2. Notes on the history of this repo September 2015: first version of this framework November 2

Matthew Flint 257 Dec 14, 2022
Efs - Easy function mocking/stubbing in Swift

Efs Efs, as the pronounced plural of letter F (for function), is a simple mockin

Jérémy Touzy 0 Feb 12, 2022
Mockingbird was designed to simplify software testing, by easily mocking any system using HTTP/HTTPS

Mockingbird Mockingbird was designed to simplify software testing, by easily mocking any system using HTTP/HTTPS, allowing a team to test and develop

FARFETCH 183 Dec 24, 2022
Swift framework containing a set of helpful XCTest extensions for writing UI automation tests

AutoMate • AppBuddy • Templates • ModelGenie AutoMate AutoMate is a Swift framework containing a set of helpful XCTest extensions for writing UI autom

PGS Software 274 Dec 30, 2022
The XCTest Project, A Swift core library for providing unit test support

XCTest The XCTest library is designed to provide a common framework for writing unit tests in Swift, for Swift packages and applications. This version

Apple 1k Jan 4, 2023
XCTestExtensions is a Swift extension that provides convenient assertions for writing Unit Test.

XCTestExtensions Features XCTAssertEventually (that convenient assertions for writing Unit Test). Use "XCTAssertEventually", you can write asynchronou

shindyu 22 Dec 1, 2022
Swift Package with examples of how to tests iOS apps

GimmeTheCodeTDD A swift package with examples of unit tests in iOS development. Requirements Xcode 13 Content Dependency Injection Constructor Injecti

Dominik Hauser 8 Oct 11, 2021
A Mac and iOS Playgrounds Unit Testing library based on Nimble.

Spry Spry is a Swift Playgrounds Unit Testing library based on Nimble. The best thing about Spry is that the API matches Nimble perfectly. Which means

Quick 327 Jul 24, 2022
Runtime introspection and unit testing of SwiftUI views

ViewInspector ??️‍♂️ for SwiftUI ViewInspector is a library for unit testing SwiftUI views. It allows for traversing a view hierarchy at runtime provi

Alexey Naumov 1.5k Jan 8, 2023