Giter VIP home page Giter VIP logo

dobby's Introduction

Dobby

Dobby provides a few helpers for mocking and stubbing.

Matchers

Matchers can be matched with values, serving as the fundamental building block for mocking and stubbing. There are many functions that help creating matchers for (equatable) types, including optionals, tuples, arrays, and dictionaries with equatable elements:

matches { $0 == value } // matches value
any() // matches anything
not(0) // matches anything but 0
none() // matches Optional<T>.None (nil)
some(1) // matches Optional<T>.Some(1)
equals(1) // matches 1
equals((1, 2)) // matches (1, 2)
equals((1, 2, 3)) // matches (1, 2, 3)
equals((1, 2, 3, 4)) // matches (1, 2, 3, 4)
equals((1, 2, 3, 4, 5)) // matches (1, 2, 3, 4, 5)
equals([1, 2, 3]) // matches [1, 2, 3]
equals([1: 1, 2: 2, 3: 3]) // matches [1: 1, 2: 2, 3: 3]

Matchers may also be nested:

matches((matches { $0 == 0 }, any(), 2)) // matches (0, _, 2)
matches((not(equals(3)), some(any()))) // matches (not(3), _)
matches([any(), equals(4)]) // matches [_, 4]
matches(["key": matches { $0 == 5 }]) // matches ["key": 5]

Mocks

Mocks can be used to verify that all set up expectations have been fulfilled.

Strict mocks

By default, mocks are strict and the order of expectations matters, meaning all interactions must be expected and occur in the order they were expected:

let mock = Mock<[Int]>()
mock.expect(matches([any(), matches { $0 > 0 }])) // expects [_, n > 0]
mock.record([0, 1]) // succeeds
mock.verify() // succeeds
mock.record([1, 0]) // unexpected, fails (fast)

The order of expectations may also be ignored:

let mock = Mock<[Int]>(ordered: false)
mock.expect(matches([0, 1]))
mock.expect(matches([1, 0]))
mock.record([1, 0]) // succeeds
mock.record([0, 1]) // succeeds
mock.verify() // succeeds
mock.record([0, 0]) // unexpected, fails (fast)

Nice mocks

Nice mocks allow unexpected interactions while still respecting the order of expectations:

let mock = Mock<[Int?]>(strict: false)
mock.expect(matches([some(0)]))
mock.expect(matches([some(any())]))
mock.record([nil]) // unexpected, ignored
mock.record([1]) // out of order, ignored
mock.record([0]) // succeeds
mock.verify() // fails
mock.record([1]) // succeeds
mock.verify() // succeeds

Of course, nice mocks can ignore the order of expectations too:

let mock = Mock<[String: Int?]>(strict: false, ordered: false)
mock.expect(matches(["zero": some(0)]))
mock.expect(matches(["none": none()]))
mock.record(["some": 1]) // unexpected, ignored
mock.record(["none": nil]) // succeeds
mock.record(["zero": 0]) // succeeds
mock.verify() // succeeds

Negative expectations

In addition to normal expectations, nice mocks allow negative expectations to be set up:

let mock = Mock<Int>(nice: true)
mock.reject(0)
mock.record(0) // rejected, fails (fast)

Verification with delay

Verification may also be performed with a delay, allowing expectations to be fulfilled asynchronously:

let mock = Mock<Int>()
mock.expect(1)

let mainQueue: DispatchQueue = .main
mainQueue.asyncAfter(deadline: .now() + 1) {
    mock.record(1) // succeeds
}

mock.verify(delay: 2) // succeeds

Stubs

Stubs, when invoked, return a value based on their set up behavior, or, if an interaction is unexpected, throw an error. Behavior is matched in order, i.e., the function or return value associated with the first matcher that matches an interaction is invoked/returned:

let stub = Stub<(Int, Int), Int>()
let behavior = stub.on(equals((2, 3)), return: 4)
stub.on(matches((any(), any()))) { x, y in x * y }
try! stub.invoke((2, 3)) // returns 4
try! stub.invoke((3, 3)) // returns 9

Behavior may also be disposed:

behavior.dispose()
try! stub.invoke((2, 3)) // returns 6

Example

The helpers provided for mocking and stubbing can be used with any testing approach, including protocol test implementations, test subclasses, etc. For example, imagine you want to verify interactions with the following class and change its behavior:

class MyClass {
  func myMethod(fst: String, _ snd: String) -> String {
    return fst + snd
  }
}

Writing a test subclass for the given class is very simple:

class MyClassMock: MyClass {
  let myMethodMock = Mock<(String, String)>()
  let myMethodStub = Stub<(String, String), String>()
  override func myMethod(fst: String, _ snd: String) -> String {
    myMethodMock.record((fst, snd))
    // Throw an exception if the stub doesn't define any behavior for the interaction.
    return try! myMethodStub.invoke((fst, snd))
  }
}

The test subclass allows you to verify that all your set up expectations are fulfilled and enables you to change its behavior on-the-fly:

let myClassMock = MyClassMock()
myClassMock.myMethodMock.expect(matches(("Hello", "World")))
myClassMock.myMethodStub.on(any()) { fst, snd in fst }
myClassMock.myMethod("Hello", "World") // returns "Hello"
myClassMock.myMethodMock.verify() // succeeds

If you ever find yourself wanting to use a mock or stub with several interactions of different types, consider using an equatable enum to define these interactions.

Documentation

Please check out the source and tests for further documentation.

About

Dobby was born at trivago ๐Ÿญ

dobby's People

Contributors

tibr avatar bckr avatar derlobi avatar mdarnall avatar susannproszak avatar

Stargazers

Anh Dung "dungi" Pham avatar

Watchers

Martin avatar James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.