Coder Social home page Coder Social logo

mockingjay's Introduction

Mockingjay

An elegant library for stubbing HTTP requests in Swift, allowing you to stub any HTTP/HTTPS using NSURLConnection or NSURLSession. That includes any request made from libraries such as Alamofire and AFNetworking.

Installation

CocoaPods is the recommended installation method.

pod 'Mockingjay'

Usage

Mockingjay has full integration to XCTest and you simply just need to register a stub, it will automatically be unloaded at the end of your test case. It will also work with the Quick behaviour-driven development framework.

Simple stub using a URI Template, returning a response with the given JSON encoded structure

let body = [ "user": "Kyle" ]
stub(uri("/{user}/{repository}"), json(body))

The uri function takes a URL or path which can have a URI Template. Such as the following:

  • https://github.com/kylef/WebLinking.swift
  • https://github.com/kylef/{repository}
  • /kylef/{repository}
  • /kylef/URITemplate.swift

Stubbing a specific HTTP method with a JSON structure

let body = [ "description": "Kyle" ]
stub(http(.put, uri: "/kylef/Mockingjay"), json(body))

Stubbing everything request to result in an error

let error = NSError()
stub(everything, failure(error))

Stub with a specific HTTP response

stub(everything, http(status: 404))

Note, the http builder can take a set of headers and a body too.

Stub

The stub method in Mockingjay takes two functions or closures, one to match the request and another to build the response. This allows you to easily extend the syntax to provide your own specific functions.

stub(matcher, builder)

Matchers

A matcher is simply a function that takes a request and returns a boolean value for if the stub matches the request.

func matcher(request:URLRequest) -> Bool {
  return true  // Let's match this request
}

stub(matcher, failure(error))

Builders

Builders are very similar to a matcher, it takes a request, and it returns either a success or failure response.

func builder(request: URLRequest) -> Response {
  let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
  return .success(response, .noContent)
}

stub(matcher, builder)

Generics

You can make use of the builtin generic matchers and builders. These can be used alone, or in conjunction with custom components.

Builtin Matchers

  • everything - Matches every given request.
  • uri(template) - Matches using a URI Template.
  • http(method, template) - Matches using a HTTP Method and URI Template.

Builtin Builders

  • failure(error) - Builds a response using the given error.
  • http(status, headers, data) - Constructs a HTTP response using the given status, headers and data.
  • json(body, status, headers) - Constructs a JSON HTTP response after serialising the given body as JSON data.
  • jsonData(data, status, headers) - Constructs a JSON HTTP response with raw JSON data.

Using JSON files as test fixtures

During setUp, load the JSON file as NSData and use jsonData.

override func setUp() {
  super.setUp()
  let url = Bundle(for: type(of: self)).url(forResource: "fixture", withExtension: "json")!
  let data = try! Data(contentsOf: url)
  stub(matcher, jsonData(data))
}

License

Mockingjay is licensed under the BSD license. See LICENSE for more info.

mockingjay's People

Contributors

antigp avatar blackm00n avatar felixclack avatar hellostu avatar hiroraba avatar iamdecode avatar itszero avatar j-j-m avatar klaaspieter avatar kylef avatar modocache avatar neonichu avatar nwest avatar shams-ahmed avatar teameh 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 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

mockingjay's Issues

Open-source

  • Open-source repository
  • Enable travis
  • Push podspec

Find out if URL was requested

Feature request:

I'd like to know whether my stubbed URL is requested.
I imagine this could work something like this:

let body = [ "description": "Kyle" ]
let userStub = stub(http(.PUT, uri: "/kylef/Mockingjay"), json(body))

expect(userStub.requested).toEventually(beTrue())

Since this doesn't work, I came up with a solution using a custom matcher:

///  Creates a custom matcher that calls the requestMatchedHandler when a request was succesfully matched
func matcher(_ method: HTTPMethod, uri: String, requestMatchedHandler: @escaping () -> Void) -> (_ request: URLRequest) -> Bool {
    let matcher = http(method, uri: uri)
    return { (request: URLRequest) in
        let result = matcher(request)
        if result {
            requestMatchedHandler()
        }
        return result
    }
}

var requestMatched = true

let body = [ "description": "Kyle" ]
stub(http(.PUT, uri: "/kylef/Mockingjay", requestMatchedHandler: { () in
    requestMatched = true
}), json(body))

expect(requestMatched).toEventually(beTrue())

NSURLSessionConfiguration swizzling with Alamofire

This is a strange one. Mockingjay's initialize() on XCTest is working as expected but the one on NSURLSessionConfiguration never actually fires.

The swizzle hooks properly if you manually call NSURLSessionConfiguration.initialize(), otherwise MockingjayProtocol never gets pushed into protocolClasses in the configuration.

Xcode 7.0 (7A220)
Alamofire 2.0.0

Here's the relevant code on Alamofire.

Carthage - Mockingjay for iOS

I use Carthage and it uses shared schemes to build dependencies but there is only a target for Mac OS X, could iOS be added so it works with Carthage?

tvOS 10+ Support

Hi everyone,

I'm currently implementing unit tests on my tvOS application and I'd like to mock HTTP requests from Apollo (GraphQL Client). I already use Mockingjay on my iOS app and I'd love to use it aswel for tvOS.

My question is quite simple, is a tvOS support coming ?

Thanks in advance for your help guys.
Btw, thanks also for your work!

Regards

CocoaPods update

Hi @kylef
please push recent changes to cocoapods. It would be much help in my future tests :)

Preventing unintended network requests for unmatched requests

In my current use of Mockingjay I find that if my implementation is somehow incorrect and the matcher returns false, actual network requests are made.

This is all working as expected, but I would prefer it if no networks requests were made, and any unintended requests cause a test to fail.

Is this somehow possible already? If not, I think something like an everythingExcept matcher could be useful.

Turn ENABLE_BITCODE = YES for watchOS result in compilation errors.

In mockingjay.xcconfig, turn ENABLE_BITCODE = YES. Turn bitcode on for all the other targets in the project too. This is required for watchOS. Please resolve this because it is a show stopper for us.

Get compilation error:

ld: '/Users/nyan/Library/Developer/Xcode/DerivedData/zipcar-bjuclsirfazpdbhbekejanyrfonv/Build/Products/Debug-iphoneos/Mockingjay/Mockingjay.framework/Mockingjay' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Xcode can't find stub or matcher now

We have jsonData now but now Xcode is complaining that stub and matcher are unavailable. Not sure why.

import Foundation
import Mockingjay

class TestModel: NSObject {

    class func testRequest() {
        let path = NSBundle.mainBundle().pathForResource("Plaid", ofType: "json")
        let data = NSData(contentsOfFile: path!)!

        stub(matcher, builder: jsonData(data))
    }

}

Ideas?

screen shot 2015-10-12 at 10 51 24 am

Simulate delay of responses

Is there any builtin functionality to add a delay to the responses, in order to simulate a real server hit?

I find it really useful for both automated UI tests and during development, in case I want to check the "loading" status with a controlled response.

Add example project

Is it possible to add an example project to see how to setup a complete project? At the moment it is not very clear how to use Mockingjay which will prevent many developers from using it.

Outdated readme.md: using PUT instead of put

Example from readme:

stub(http(.PUT, "/kylef/Mockingjay"), json(body))

Current version of Alamofire uses .put, not .Put. Example is also missing uri label. This produces compilation errors.

URITemplate Broke

Hi URITemplate is not yet compatible with swift 1.2. Can you link to the new version in pod spec when it comes out?

Isn't better use URITemplate 2.0.0 in last tag?

In 1.3.0 release and master branch you're using URI Template 2.0.0, but when I use the last tag (1.3.1) the URITemplate dependency used is 1.4.0. Isn't better use also version 2.0.0 in last tag?

Cannot stub a uri without complete url scheme

Using mockingjay to stub api calls that can be done on any baseUrl, I am doing this:
self.stub(http(.PUT, uri: "/session/{session_id}/rate?user_id={user_id}"), builder: http(400))

This does not work, I need to do this:
self.stub(http(.PUT, uri: "http://demo.usievents.com/api/v1/session/42/rate?user_id=42"), builder: http(400))

Digging into URITemplate.swift, it seems you add a "^" in front of the regex, thus it expects the matching to occurs on first character.

Getting Library not loaded

I'm getting the following error in the console but am at a miss to fix it.
Can someone help?

dyld: Library not loaded: @rpath/XCTest.framework/XCTest
  Referenced from: /Users/Dev/Library/Developer/Xcode/DerivedData/test-hehyhfozbszkqvgifetexjhfsqsz/Build/Products/Debug-iphonesimulator/Mockingjay.framework/Mockingjay
  Reason: image not found

Minimum deployment target 9.1 error Xcode 7.1

I've just upgraded my Xcode to 7.1 and I'm no longer being able to build Mockingjay due to the following error: Module file's minimum deployment target is ios9.1 v9.1:
Do you have any idea how to fix this?
Thanks.

Tries to load library to wrong target

I'm developing a framework in swift, I'm using Carthage for dependency management. When I add Mockingjay to the test target my tests work, but when I try to build an app with my framwork i get

dyld: Library not loaded: @rpath/Mockingjay.framework/Mockingjay

Which is strange, as Mockingjay is only added to the test target.

Reproducible example can be found at:

https://github.com/tapglue/ios_sdk/tree/mockingjay

Using Mockingjay in app target (no XCTest)

I want to use Mockingjay in my app to stub real APIs while they are still in development, so I tried what @kylef suggested in #28, but it is not working because you also need to swizzle the NSURLSessionConfiguration (as the implementation in Mockingjay/XCTest does).

The final replacement stub method should be:

func stub(matcher:Matcher, builder:Builder) -> Stub {
    NSURLSessionConfiguration.mockingjaySwizzleDefaultSessionConfiguration()    
    return MockingjayProtocol.addStub(matcher, builder: builder)
}

Since using Mockingjay without XCTest could be a useful scenario, maybe it is worth having a note in the readme, or even better supporting this natively.

Another issue is that using the full Mockingjay pod in a test target while using Mockingjay/Core pod in the app creates conflicts on compilation, since if you include both pods they will be compiled as a single framework, I guess (maybe Cocoapods error?).
In my opinion, the stub method should be moved to another global place (like Mockingjay.stub), so that it would be available regardless of XCTest, while the XCTest stuff should be moved to a separate framework or even removed entirely, since it is so simple (and much more clear) to just add a removeAllStubs on tearDown method in a test case, like OHHTTPStubs does.

Empty Body

How can i stub a request with only status code? Without body.

I've been trying to do that but with no success.

I did this

stub(http(method.toMockingjay(), uri: endpointPath), http(401))

And a do need to get the statusCode but the DataResponse.response from Alamofire is always nil

Matchers running slowly

I've got a test that's a sort of integration/stress test for my networking code. When I switched from having my networking code check for cached files my tests placed in a temp directory to using Mockingjay, the tests slowed down a lot. I profiled with Instruments, and found that most of the test's time is being spent in MockingjayProtocol.stubForRequest(NSURLRequest) -> Stub?, and specifically in my custom matcher, which looks like this:

public func urlPathEquals(path: String) -> (request:NSURLRequest) -> Bool {
    return { request in
        request.URL?.path == path
    }
}

Before this test case runs, I loop through my large number of JSON files and create a matcher like this for each one, so there is a large number of mocks configured. What can I do to speed this up? My test is taking so long that it's causing a failure on my build server.

Add support for Swift 3/Xcode 8

I've attempted to convert the project over using the Xcode 8 refactor tool, and fixed a few things manually. I am having trouble with the MockingjayAsyncProtocolTests. Those tests will show a number 2-second timeout messages, and then just seems to sit there and not won't complete the test. All of the other tests pass fine.

My code is on my fork, adamdebono/Mockingjay@bfc0e71

(Also I have pointed URITemplate to my own fork which has also been updated to Swift 3 as well)

Tag 1.0.0 Release

I'm sure you'll find this issue a doddle to fix!

I'm having trouble using v1.0.0 via CocoaPods because there's no 1.0.0 tag on this repository.

Just in case you need this, this is what pod install says:

[!] Error installing Mockingjay
[!] /usr/local/bin/git clone https://github.com/kylef/Mockingjay.git /var/folders/1f/j5dkbzz11wq7r6wpy0zl38t00000gn/T/d20150922-6859-12gu0sb --single-branch --depth 1 --branch 1.0.0

Cloning into '/var/folders/1f/j5dkbzz11wq7r6wpy0zl38t00000gn/T/d20150922-6859-12gu0sb'...
warning: Could not find remote branch 1.0.0 to clone.
fatal: Remote branch 1.0.0 not found in upstream origin

Carthage support

I'm just wondering if this project is open to putting official support for Carthage. Perviously it was possible to get Mockingjay (sort of) working with Carthage just by adding both Mockingjay and URITemplate to the Cartfile.

But moving to Swift 3 support and this no longer works.

One of the reasons is that URITemplate has now removed the Xcode project file for the repo which Carthage relies on to build the framework. This means that if we are going to add Carthage support for this, we will probably need to restore the project file in the URITemplate repo.

The other issue is that, as I mentioned above, the previous method only sort of worked. I'm not 100% sure on this, but because this repo includes URITemplate as a submodule, when Carthage checkouts/clones this repo as a dependency, it also checkouts the URITemplate submodule. So I think that when the project is built, it is using the commit/version of URITemplate that the submodule points to. Then when used in our test target, it uses a different version of URITemplate framework which is built using the commit/version that is resolved by Carthage (which might no necessarily be the same as the one that is pointed to by the URITemplate submodule in this repo).

TBH, I'm not necessarily sure what the best way is to resolve the issue above. A solution might involve changing the location of the URITemplate project to the Carthage/Checkout/URITemplate path, and using Carthage to checkout the dependencies before building the project in Xcode.

Thoughts?

jsonData in Pod release

Hey Kyle,

Great work. Just wanted to point out that the jsonData builder isn't in the CocoaPod release, in case you didn't know.

Thanks

Add example project

Is it possible to add an example project to see how to setup a complete project? At the moment it is not very clear how to use Mockingjay which will prevent many developers from using it.

Swift 3 + Alamofire 4

Tests were failing which was due to Alamofire’s SessionManager’s URLSessionConfiguration not having MockingjayProtocol in its list of URLProtocol classes. Turns out the swizzler was never being called. I fixed this in a fork.

Not sure if you had already planned on fixing this or had something else in mind to address it.

request.HTTPBody is always nil in custom matcher

I'm setting the HTTPBody property of an NSMutableURLRequest and trying to do some comparison on it inside of a custom matcher.

However, the property's value is always nil by the time the request reaches the matcher despite it being set prior to that.

Swift 4 Support - Podspec update

I see the changes for Swift 4 have been made. However, the pod spec hasn't been updated so this refactor remains unavailable on Cocoapods.

Compare query parameters (ignoring order)

I think it'd be useful to have a matcher (or change uri) to compare query parameters ignoring their order. I was able to implement that with a custom matcher:

func uriWithQuery(uriString:String) -> (request:NSURLRequest) -> Bool {
    return { (request:NSURLRequest) in
        if uri(uriString)(request: request) {
            return true
        }

        if let components = NSURLComponents(string: uriString),
            requestComponents = request.URL.flatMap({ NSURLComponents.init(URL: $0, resolvingAgainstBaseURL: false)}) {
            let componentsWithoutParams = components.copy() as! NSURLComponents
            componentsWithoutParams.query = nil

            let requestComponentsWithoutParams = requestComponents.copy() as! NSURLComponents
            requestComponentsWithoutParams.query = nil

            if requestComponentsWithoutParams.URL == componentsWithoutParams.URL {
                return requestComponents.queryItems.flatMap(Set.init) == components.queryItems.flatMap(Set.init)
            }
        }

        return false
    }
}

Note that this probably doesn't support path templates.

Problem using MockingJay

I have a model class that fetches data from my server using AlamoOnFire, ObjectMapper and Realm for persistance. Realm introduces the List construct.

My problem is that MockingJay doesn't seem to be used. The JSON-File from the file-system can be loaded in my test and contains the data that is computed correctly if fetched from server.

Any idea what could be wrong?

import XCTest
import Mockingjay
import RealmSwift
@testable import MyApp

class OverviewModelTests: XCTestCase {

    override func setUp() {
        super.setUp()
        let path = NSBundle(forClass: self.dynamicType).pathForResource("Overview", ofType: "json")!
        let data = NSData(contentsOfFile: path)!
        stub(everything, builder: jsonData(data))
    }
    //http(.GET, uri: URLs.sharedInstance.overviewItemsUrl())


    func testFetchOverviewItems() {

        let overviewModel = OverviewModel()

        overviewModel.fetchOverviewItemList({ (itemList: List<OverviewItem>) -> () in

            XCTAssertTrue(itemList.count == 6)
            })
            { (error: ErrorType?) -> () in
                XCTFail("Error in testFetchOverviewItems ")
            }
    }


}

Add instructions how to install using Carthage

Thank you for this great project. I was thinking about doing this myself.

I don't want to use CocoaPods. Could you please add instructions how to install it manually of using Carthage.

I tried to build it using Carthage but it didn't work. The compiler doesn't find 'stub'.

dyld: lazy symbol binding failed: Symbol not found

I have been using Mockingjay in XCTest very well. However recently I noticed that if I can use it in main target, it would be nice. So I changed Podfile like this:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!

inhibit_all_warnings!

target 'Surfline' do
    pod 'Mockingjay/Core'

    target 'SurflineTests' do
        pod 'Mockingjay/XCTest'
    end
end

And it worked very well with main target but I stuck in test.

I have this line in my test

@testable import MainTarget

I need this for not to change access level of class properties in the main target. Tests without the line is perfectly fine but with the line it shows error:

dyld: lazy symbol binding failed: Symbol not found: __TFE10MockingjayCSo6XCTest4stubfTFV10Foundation10URLRequestSbFS2_OS_8Response_VS_4Stub
  Referenced from: /Users/rhan/Library/Developer/Xcode/DerivedData/Surfline-eheubjimlqgiykerufbqqnwywuhs/Build/Intermediates/CodeCoverage/Products/Debug-iphonesimulator/Surfline.app/PlugIns/SurflineTests.xctest/SurflineTests
  Expected in: /Users/rhan/Library/Developer/CoreSimulator/Devices/9C5026F0-B25E-40C1-99FB-0E1F63FAFC3F/data/Containers/Bundle/Application/734A040B-E1D2-45F8-93A4-3ADFA837010B/Surfline.app/Frameworks/Mockingjay.framework/Mockingjay

dyld: Symbol not found: __TFE10MockingjayCSo6XCTest4stubfTFV10Foundation10URLRequestSbFS2_OS_8Response_VS_4Stub
  Referenced from: /Users/rhan/Library/Developer/Xcode/DerivedData/Surfline-eheubjimlqgiykerufbqqnwywuhs/Build/Intermediates/CodeCoverage/Products/Debug-iphonesimulator/Surfline.app/PlugIns/SurflineTests.xctest/SurflineTests
  Expected in: /Users/rhan/Library/Developer/CoreSimulator/Devices/9C5026F0-B25E-40C1-99FB-0E1F63FAFC3F/data/Containers/Bundle/Application/734A040B-E1D2-45F8-93A4-3ADFA837010B/Surfline.app/Frameworks/Mockingjay.framework/Mockingjay

I'm stuck here. Any hint will be very appreciated.

Can I mock language headers?

Can I use this lib mock "Accept-Language" with "en-US;q=1.0" value rather than the system (iphone or MAC) value?
Thanks a lot.

Offer JSON Schema validation

Making use of JSONSchema to provider a matcher.

This might look something like:

let schema = Schema([
    "type": "object",
    "properties": [
        "name": ["type": "string"],
        "price": ["type": "number"],
    ],
    "required": ["name"],
])

let matcher = allOf([
    uri('/'),
    json(schema),
])
stub(matcher, response)

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.