Giter Site home page Giter Site logo

leoture / mockswift Goto Github PK

View Code? Open in Web Editor NEW
89.0 8.0 7.0 1.89 MB

MockSwift is a Mock library written in Swift.

Home Page: https://adoring-varahamihira-bbe99d.netlify.app/documentation/

License: MIT License

Dockerfile 0.02% Swift 99.07% HTML 0.91%
swift mock test swift-package-manager xcode playground

mockswift's Introduction

MockSwift

Welcome to MockSwift

Release Build Status Quality Gate Status Netlify Status Swift Package Manager compatible Swift license MIT

MockSwift allows you to write mocks and 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.

Table of Contents

Installation

Swift Package Manager

MockSwift has been designed to work with Swift Package Manager.

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

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.

struct User: Equatable {
  let identifier: String
  let name: String
}

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

And you want to test this UserCore class.

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.

@Mock private var service: UserService

// equivalent to

private var service: UserService = Mock()

And easly configure it to fully test UseCore.

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:

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

// equivalent to

given(service) {
  $0.fetch(identifier: .any()).willReturn(expectedUser)
}
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.

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

Then

then() enables you to verify calls.
example:

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

// equivalent to

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

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

You can go further and verify order of calls

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.

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.

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

@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 for sourcery. You can use the AutoMockable annotation to generate code.

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

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.

extension Given where WrappedType == UserService {
  public func fetch(identifier: Predicate<String>) -> Mockable<User> {
    mockable(identifier)
  }
  public func fetch(identifier: String) -> Mockable<User> {
    mockable(identifier)
  }
}

To allow call verification through then() method, you have to extend Then.

extension Then where WrappedType == UserService {
  public func fetch(identifier: Predicate<String>) -> Verifiable<User> {
    verifiable(identifier)
  }
  public func fetch(identifier: String) -> Verifiable<User> {
    verifiable(identifier)
  }
}

Documentation

If you need more details about the API, you can check out our API documentation.

Contribution

Would you like to contribute to MockSwift? Please read our contributing guidelines and code of conduct.

License

MockSwift is released under the MIT license. See LICENSE for details.

Credits

Thanks to JetBrains

mockswift's People

Contributors

ailtonvivaz avatar leoture avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mockswift's Issues

Add Interaction.ended()

πŸš€ Describe the feature you'd like

interaction(with: service).ended()
Should pass if all calls have been verified

πŸ›  Describe the implementation you've considered

need to delete #97

Add Predicate identical

πŸš€ Describe the feature you'd like

New predicate based on the operator ===

πŸ›  Describe the implementation you've considered

public extension Predicate where Input: AnyObject {
    static func identical(to value: Input) -> Predicate<Input> {
        .match(description: "\(value)") { $0 === value }
    }
}

same thing for
=== and id()

Predicate .match with KeyPath

πŸš€ Describe the feature you'd like

public class func match(description: String = "custom matcher", _ keyPath: KeyPath<Input, Bool>) -> Predicate<Input>

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

then() with WrappedType

πŸš€ Describe the feature you'd like

then(_mock) -> then(mock)

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

then() with completion block

πŸš€ Describe the feature you'd like

then(mock) {
$0.doSomething().called{ }
$0.doOtherThing().called{ }
}

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Add neverCalled()

πŸš€ Describe the feature you'd like

then(service).fetch(identifier: .any()).neverCalled()

πŸ›  Describe the implementation you've considΓ¨re

neverCalled() = .called(0)

Mock init with Defaults

πŸš€ Describe the feature you'd like

@Mock(localStubs: [
  String.self => "value"
]) private var service: Service

πŸ›  Describe the implementation you've considered

TBD

ℹ️ Other information

Fix parameters annotations with sourcery

🐞 Describe the bug

When a parameter is annotated with @escaping for exemple, sourcery will generate in MockGiven and MockThen a predicate Predicate<@escaping ...>for this parameter.

πŸ“ To Reproduce

func doSomething(arg: @escaping (String) -> Bool)
will generate
public func doSomething(arg: Predicate<@escaping (String) -> Bool>) -> Mockable<Void>

πŸ” Expected behavior

public func doSomething(arg: Predicate<(String) -> Bool>) -> Mockable<Void>

Fix access level

🐞 Describe the bug

public static func match(_ value: Input, file: StaticString = #file, line: UInt = #line) -> AnyPredicateshall not be public.

Add Predicate .isNil

πŸš€ Describe the feature you'd like

A clear and concise description of what you want to happen.

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

InOrder

πŸš€ Describe the feature you'd like

@Mock private var service1: Service1
@Mock private var service2: Service2
@Mock private var service3: Service3
...
let _: String = service1.fetch(identifier: "")
let _: String = service2.fetch(identifier: "")
let _: String = service3.fetch(identifier: "")

let inOrder = InOrder()
then(service).fetch(identifier: .any()).called(inOrder) // pass
then(service3).fetch(identifier: .any()).called(inOrder) // pass
then(service2).fetch(identifier: .any()).called(inOrder) // fail "no call to Service2.fetch(identifier:) -> String found after the call to Service3.fetch(identifier:) -> String"

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

call mocked with optionals

πŸš€ Describe the feature you'd like

A clear and concise description of what you want to happen.

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Generate Mock, MockGiven, MockThen extensions

πŸš€ Describe the feature you'd like

A template MockSwift.stencil for sourcery. It will generate the required extensions for all protocoltypes.

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Add Predicate .isTrue & .isFalse

πŸš€ Describe the feature you'd like

A clear and concise description of what you want to happen.

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Strategy configuration

πŸš€ Describe the feature you'd like

Be able to change the order of strategy used by the Mock.

@Mock(strategy: [.given, .localStubs, .globalStubs])
private var custom: Custom

πŸ›  Describe the implementation you've considered

 convenience init(strategy: [StrategyIdentifier] = .default,
                  stubs: [Stub] = [],
                  _ completion: (MockGiven<WrappedType>) -> Void = { _ in })

StrategyIdentifier.default Γ©quivalent to [StrategyIdentifier.given, StrategyIdentifier.localStubs, StrategyIdentifier.globalStubs]

MockGiven willThrow

πŸš€ Describe the feature you'd like

given(mock).function().willThrow(MyError())

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Remove Predicate.match with KeyPath

πŸš€ Describe the feature you'd like

Predicate from KeyPath is no longer required. see SE-0249

πŸ›  Describe the implementation you've considered

Remove match(description: String? = nil, any type: Input.Type = Input.self, when keyPath: KeyPath<Input, Bool>)

Predicate not

πŸš€ Describe the feature you'd like

public class func not(_ predicate: Predicate<Input>) -> Predicate<Input>

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

given() with WrappedType

πŸš€ Describe the feature you'd like

given(_mock) -> given(mock)

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Mock subscripts

πŸš€ Describe the feature you'd like

// Service
subscript(arg: String) -> Bool

//Test
given(service) {
      $0[.any()].get.willReturn(true)
      $0[""].set(.isFalse()).will { ... }
    }

then(basicSubscript) {
      $0[.any()].get.called()
      $0[""].set(false).called()
}

Compilation error in generated swift file when protocol have init method. How to use init in protocol?

I have init method in my protocol. I am getting the below error when sourcery generates the code.
Keyword 'init' cannot be used as an identifier here. If this name is unavoidable, use backticks to escape it

πŸ“ For Example

// sourcery: AutoMockable
protocol DashboardRepository: AnyObject {
    func fetchDashBoard() -> [DashboardItem]
    func updateNotification()
    init(apiclient: APIClient)
}

Sourcery generated the code as below:

public func init(apiclient: APIClient) -> DashboardRepository {
    mocked(apiclient)
  }
.......

πŸ“ What is the Question?

How to generate the mock when protocol have init method?

Add VerifiableProperty

πŸš€ Describe the feature you'd like

Allow to verify property calls.

protocol Service {
  var id: String { get set }
}
...
class ServiceTests: XCTestCase {
  @Mock private var service: Service

  func myTest() {
    ...

    // Then
    then(service) {
      $0.id.get.called()
      $0.id.set("id2").called()
    }
  }

πŸ›  Describe the implementation you've considered

extension MockThen where WrappedType == Service {
  var id: VerifiableProperty<String> { verifiable() } // ⚠️ #function = id
}

VerifiableProperty.get should return a Mockable<String> with functionIdentifier = id() -> String in this case.
VerifiableProperty.set should return a Mockable<Void> with functionIdentifier = id(newValue:) -> Void in this case.

Generate Mock only for AutoMockables

πŸš€ Describe the feature you'd like

Generate Mock only for protocol annotated with ///sourcery: AutoMockable

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

'Float80' is unavailable: Float80 is not available on target platform.

🐞 Describe the bug

When MockSwift is used in a Mac with ARM arch (Apple Silicon), following error ocurred:
'Float80' is unavailable: Float80 is not available on target platform.

πŸ“ To Reproduce

Try use the library or build in a Mac with ARM

πŸ” Expected behavior

The tests builds with no errors.

βš™οΈ Environment

MockSwift version: 1.0.0
OS:

  • macOS 11.6.1
  • Linux x.x.x

Swift version: 5.5.2
Xcode version: 13.2.1

ℹ️ Other information

As discussed in Swift Community, a solution for this would be add this compile condition:
#if !os(Windows) && (arch(i386) || arch(x86_64))

Generate Mock with methods that throw

πŸš€ Describe the feature you'd like

///sourcery: AutoMockable
private protocol Custom {
  func function(identifier: String) throws
  func function(identifier: String) throws -> String
}

Generated Mock should be

extension Mock: Custom where WrappedType == Custom {
  func function(identifier: String) throws { try mockedThrowable(identifier) }
  func function(identifier: String) throws -> String { try mockedThrowable(identifier) }
}

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Disambiguate predicates

πŸš€ Describe the feature you'd like

func doSomething<T>(value: Predicate<T>)
actually:

  • doSomething(.any())
  • doSomething(.match(\.isEmpty))
  • doSomething(.match{ $0.isEmpty })
    Don't compile because T could not be inferred.

So we need to disambiguate T with:

  • doSomething(.any(String.self))
  • doSomething(.match(any: String.self, when: \.isEmpty))
  • doSomething(.match(any: String.self) { $0.isEmpty })

πŸ›  Describe the implementation you've considered

public class func match(description: String, any: Input.Type, when keyPath: KeyPath<Input, Bool>) -> Predicate<Input>

public static func any(_ type: Input.Type) -> Predicate<Input>

public static func match(description: String, any: Input.Type, when predicate: @escaping (Input) -> Bool) -> Predicate<Input>

for example:

public static func any(_ type: Input.Type) -> Predicate<Input> {
  .any()
}

More GlobalStubs

This issue will be split.

New GlobalStubs (To complete)

  • Int8 = 0
  • Int16 = 0
  • Int32 = 0
  • Int64 = 0
  • UInt = 0
  • UInt8 = 0
  • UInt16 = 0
  • UInt32 = 0
  • UInt64 = 0
  • Float80 = 0
  • Array = []
  • Dictionary = [:]
  • Set = []
  • Character = Character("")
  • Date = Date(timeIntervalSince1970: 0)

ArgumentCaptor

πŸš€ Describe the feature you'd like

// func doSomething(arg1: String, arg2: Int)

mock. doSomething(arg1: "hello", arg2: 1)
mock. doSomething(arg1: "world", arg2: 2)
mock. doSomething(arg1: "!", arg2: 3)

let captor = ArgumentCaptor<String>()

then(mock)
    .doSomething(arg1: captor, arg2: >=2)

arg1.values // ["word", "!"]

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Add willDoNothing()

πŸš€ Describe the feature you'd like

given(mock).methodReturnVoid(arg1,arg2).willDoNothing()

πŸ›  Describe the implementation you've considered

extend Mockable with willDoNothing().
equivalent to .will { _ in }

Mock init with block

πŸš€ Describe the feature you'd like

@Mock({
    $0.fetch(identifier: .any()).willReturn("value")
  }) private var service: Service

πŸ›  Describe the implementation you've considered

Add a new Mock.init

public convenience init(_ completion: (MockGiven<WrappedType>) -> Void) {
  self.init()
  given(wrappedValue, completion)
}

Add MockableProperty

πŸš€ Describe the feature you'd like

Allow to define property behaviour.

protocol Service {
  var id: String { get set }
}
...
class ServiceTests: XCTestCase {
  @Mock private var service: Service

  func myTest() {
    // Given
    given(service) {
      $0.id.get.willReturn("id")
      $0.id.set("id2").will { ... }
    }

    ...
  }

πŸ›  Describe the implementation you've considered

extension MockGiven where WrappedType == Service {
  var id: MockableProperty<String> { mockable() } // ⚠️ #function = id
}

MockableProperty.get should return a Mockable<String> with functionIdentifier = id() -> String in this case.
MockableProperty.set should return a Mockable<Void> with functionIdentifier = id(newValue:) -> Void in this case.

given() with a completion block

πŸš€ Describe the feature you'd like

given(mock) {
$0.doSomething().will{ }
$0.doOtherThing().will{ }
}

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Migrate to Xcode 11 & swift 5.1 release

πŸš€ Describe the feature you'd like

A clear and concise description of what you want to happen.

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

.willReturn() with a list of return values

πŸš€ Describe the feature you'd like

// func myFunction() -> Int
given(mock)
    .myFunction()
    .willReturn([1, 2, 3])

mock.myFunction() // 1
mock.myFunction() // 2
mock.myFunction() // 3
mock.myFunction() // 3
.... //3

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Predicates functions for Comparable

πŸš€ Describe the feature you'd like

public prefix func >=<Input: Comparable>(rhs: Input) -> Predicate<Input>
public prefix func <=<Input: Comparable>(rhs: Input) -> Predicate<Input>
public prefix func <<Input: Comparable>(rhs: Input) -> Predicate<Input>

πŸ›  Describe the implementation you've considered

A clear and concise description of any implementation solutions you've considered.

ℹ️ Other information

Unable to get started with Mocking using MockSwift.

I created a very simple protocol which I want to mock.

protocol CalculatorServiceProtocol {
    func add(a: Int, b: Int) -> Int
}

In my test I write the following:

final class when_user_performs_add_operation_using_calculator: XCTestCase {

    @Mock private var calculatorService: CalculatorServiceProtocol
    
    func test_should_add_successfully() {
       
        // expectation
        given(calculatorService).??? // how to call the add method and return value
        
        }
}

My confusion is that how do I call the add method on the CalculatorServiceProtocol. The .add method never shows up.

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.