🐤Dynamically Mock server behaviors and responses in Swift

Overview

Kakapo partyparrot

Language: Swift Build Status Version DocCov codecov codebeat badge License Platform Carthage compatible

Dynamically Mock server behaviors and responses.

Contents

Kakapo is a dynamic mocking library. It allows you to replicate your backend APIs and logic.
With Kakapo you can easily prototype your application based on your API specifications.

Why Kakapo?

A common approach when testing network requests is to stub them with fake network responses from local files or recorded requests. This has some disadvantages:

  • All files need to be updated when the APIs are updated.
  • Lots of files have to be generated and included in the project.
  • Are just static responses that can only be used for unit tests since they don't reflect backend behaviors and state.

While still this approach may work good, Kakapo will be a game changer in your network tests: it will give you complete control when it comes to simulating backend behaviors. Moreover, is not just unit testing: you can even take a step further and prototype your application before having a real service behind!
With Kakapo you can just create Swift structs/classes/enums that are automatically serialized to JSON.

7 billion people on Earth

Less than 150 Kakapo

Time is critical donate to Kakapo recovery

Features

  • Dynamic mocking
  • Prototyping
  • Swift 3.0 compatible (from version 2.0.0, master branch)
  • Swift 2.2 and 2.3 compatible (from version 1.0.0, branch feature/legacy-swift)
  • Compatible with Platform
  • Protocol oriented and pluggable
  • Fully customizable by defining custom serialization and custom responses
  • Out-of-the-box serialization
  • JSONAPI support

Installation

Using CocoaPods:

use_frameworks!
pod 'Kakapo'

Using Carthage:

github "devlucky/Kakapo"

Usage

NOTE: The project also contains a README.playground. Check it out to see some examples of the key features of Kakapo.

Kakapo is made with an easy-to-use design in mind. To quickly get started, you can create a Router that intercepts network requests like this:

let router = Router.register("http://www.test.com")
router.get("/users") { request in
  return ["id" : 2, "name": "Kakapo"]
}

You might be wondering where the dynamic part is; here is when the different modules of Kakapo take place:

let store = Store()
store.create(User.self, number: 20)

router.get("/users") { request in
  return store.findAll(User.self)
}

Now, we've created 20 random User objects and mocked our request to return them.

Let's get a closer look to the different features:

Serializable protocol

Kakapo uses the Serializable protocol in order to serialize objects to JSON. Any type can be serialized as long as it conforms to this protocol:

struct User: Serializable {
  let name: String
}

let user = User(name: "Alex")
let serializedUser = user.serialized()
//  -> ["name": "Alex"]

Also, standard library types are supported: this means that Array, Dictionary or Optional can be serialized:

let serializedUserArray = [user].serialized()
// -> [["name": "Alex"]]
let serializedUserDictionary = ["test": user].serialized()
// -> ["test": ["name": "Alex"]]

Router - Register and Intercept

Kakapo uses Routers in order to keep track of the registered endpoints that have to be intercepted.
You can match any relative path from the registered base URL, as long as the components are matching the request's components. You can use wildcard components:

let router = Router.register("http://www.test.com")

// Will match http://www.test.com/users/28
router.get("/users/:id") { ... }

// Will match http://www.test.com/users/28/comments/123
router.get("/users/:id/comments/:comment_id") { ... }

The handler will have to return a Serializable object that will define the response once the URL of a request is matched. When a Router intercepts a request, it automatically serializes the Serializable object returned by the handler and converts it to Data.

router.get("/users/:id") { request in
  return ["id": request.components["id"]!, "name": "Joan"]
}

Now everything is ready to test your mocked API; you can perform your request as you usually would do:

let session = URLSession.shared
let url = URL(string: "http://www.test.com/users/1")!
session.dataTask(with: url) { (data, _, _) in
    // handle response
}.resume()

Note: query parameters are not affecting the route match http://www.test.com/users/1?foo=bar would also be matched

In the previous example the handler was returning a simple Dictionary; while this works because Dictionary is already Serializable, you can also create your own entities that conform to Serializable:

struct User: Serializable {
    let firstName: String
    let lastName: String
    let id: String
}

router.get("/users/:id") { request in
  return User(firstName: "Joan", lastName: "Romano", id: request.components["id"]!)
}

When a request is matched, the RouteHandler receives a Request object that represents your request including components, query parameters, httpBody and httpHeaders. The Request object can be useful when building dynamic responses.

Third-Party Libraries

Third-Party libraries that use the Foundation networking APIs are also supported but you might need to set a proper URLSessionConfiguration.
For example, to setup Alamofire:

let configuration = URLSessionConfiguration.default
configuration.protocolClasses = [Server.self]
let sessionManager = SessionManager(configuration: configuration)

Leverage the Store - Dynamic mocking

Kakapo gets even more powerful when using your Routers together with the Store. You can create, insert, remove, update or find objects.

This lets you mock the APIs behaviors as if you were using a real backend. This is the dynamic side of Kakapo.

To create entities that can be used with the store, your types need to conform to the Storable protocol.

struct Article: Storable, Serializable {
    let id: String
    let text: String

    init(id: String, store: Store) {
        self.id = id
        self.text = randomString() // you might use some faker library like Fakery!
    }
}

An example usage could be to retrieve a specific Article:

let store = Store()
store.create(Article.self, number: 20)

router.get("/articles/:id") { request in
  let articleId = request.components["id"]!
  return store.find(Article.self, id: articleId)
}

Of course you can perform any logic which fits your needs:

router.post("/article/:id") { request in
    return store.insert { (id) -> Article in
        return Article(id: id, text: "text from the body")
    }
}

router.del("/article/:id") { request in
  let articleId = request.components["id"]!
  let article = store.find(Article.self, id: articleId)!
  try! store.delete(article)

  return ["status": "success"]
}

CustomSerializable

In Serializable we described how your classes can be serialized. The serialization, by default, will Mirror (using Swift's reflection) an entity by recursively serializing its properties.

Whenever a different behavior is needed, you can instead conform to CustomSerializable to provide your custom serialization.

For instance, Array uses CustomSerializable to return an Array containing its serialized elements. Dictionary, similarly, is serialized by creating a Dictionary with the same keys and serialized values.

For other examples of CustomSerializable and how to use it to create more complex serializations, take a look at the JSONAPISerializer implementation.

JSONAPI

Since Kakapo was built with JSONAPI support in mind, JSONAPISerializer is able to serialize your entity into JSON conforming to jsonapi.org.

Your entities, in order to be serialized conforming to JSONAPI, need to conform to JSONAPIEntity protocol.

Let's see an example:

struct Cat: JSONAPIEntity {
    let id: String
    let name: String
}

struct User: JSONAPIEntity {
    let id: String
    let name: String
    let cats: [Cat]
}

Note that JSONAPIEntity objects are already Serializable and you could just use them together with your Routers. However, to completely follow the JSONAPI structure in your responses, you should wrap them into a JSONAPISerializer struct:

router.get("/users/:id") { request in
  let cats = [Cat(id: "33", name: "Joan"), Cat(id: "44", name: "Hez")]
  let user = User(id: "11", name: "Alex", cats: cats)
  return JSONAPISerializer(user)
}

Expanding Null values with Property Policy

When serializing to JSON, you may want to represent a property value as null. For this, you can use the PropertyPolicy enum. It is similar to Optional, providing an additional .null case:

public enum PropertyPolicy<Wrapped>: CustomSerializable {
    case None
    case Null
    case Some(Wrapped)
}

It's only purpose is to be serialized in 3 different ways, to cover all possible behaviors of an Optional property. PropertyPolicy works exactly as Optional properties:

  • .none -> property not included in the serialization
  • .some(wrapped) -> serialize wrapped

The additional case ,.null, is serialized as null when converted to json.

PropertyPolicy<Int>.none.serialized() // nil
PropertyPolicy<Int>.null.serialized() // NSNull
PropertyPolicy<Int>.some(1).serialized() // 1

Key customization - Serialization Transformer

The keys of the JSON generated by the serialization are directly reflecting the property names of your entities. However, you might need different behaviors. For instance, many APIs use snake_case keys but almost everyone use camelCase properties in Swift.
To transform the keys you can use SerializationTransformer. Objects conforming to this protocol are able to transform the keys of a wrapped object at serialization time.

For a concrete implementation, check SnakecaseTransformer: a struct that implements SerializationTransformer to convert keys into snake case:

let user = User(userName: "Alex")
let serialized = SnakecaseTransformer(user).serialized()
print(serialized) // [ "user_name" : "Alex" ]

Customize responses with ResponseFieldsProvider

If your responses need to specify status code (which will be 200 by default) and/or header fields, you can take advantage of ResponseFieldsProvider to customize your responses.

Kakapo provides a default ResponseFieldsProvider implementation in the Response struct, which you can use to wrap your Serializable objects:

router.get("/users/:id"){ request in
    return Response(statusCode: 400, body: user, headerFields: ["access_token" : "094850348502"])
}

let url = URL(string: "http://www.test.com/users/2")!
session.dataTaskWithURL() { (data, response, _) in
    let allHeaders = response.allHeaderFields
    let statusCode = response.statusCode
    print(allHeaders["access_token"]) // 094850348502
    print(statusCode) // 400
}.resume()

Otherwise your Serializable object can directly implement the protocol: take a look at JSONAPIError to see another example.

Roadmap

Even though Kakapo is ready to use, it is not meant to be shipped to the App Store although you can also do it! In fact, you might see it in action in some Apple stores since it was used to mock some features of Runtastic's demo app; however, it's at its early stage and we would love to hear your thoughts. We encourage you to open an issue if you have any questions, feedbacks or you just want to propose new features.

  • Full JSON API support #67
  • Reverse and Recursive relationships #16
  • Custom Serializers for common json specifications

Examples

Newsfeed BuddyBuild

Make sure you check the demo app we created using Kakapo: a prototyped newsfeed app which lets the user create new posts and like/unlike them.
To quickly try it use: pod try Kakapo

Authors

@MP0w - @zzarcon - @joanromano

Comments
  • Implement NSURLProtocol.stopLoading() for delayed requests

    Implement NSURLProtocol.stopLoading() for delayed requests

    General description

    Implemented the NSURLProtocol#stopLoading() logic in KakapoServer

    After the latency feature has been added to Router, which allows to delay the request callback reporting, it is now possible that a request may be waiting for its callback, but KakapoServer gets commanded to cancel the particular request.

    This pull requests makes the full roundtrip and adds the capability to "not complete" the request, if KakapoServer command so.

    Primarily affected components

    • KakapoServer & Router

    Depending components

    • none

    Expected possible Pitfalls

    • none

    Device and OS test coverage

    • Test on iPhone 5, iOS 8: untested
    • Test on iPhone 6S, iOS 9: leviathan
    • Test on iPad, iOS 8: untested
    • Test on iPad, iOS 9: untested
    • Test on Watch, watchOS 2: untested

    Closes #88

    opened by leviathan 17
  • Feature/query parameter router matching

    Feature/query parameter router matching

    I recently stumbled upon a Service API, where the routing was done exclusively with the query parameters.

    While that is not that pretty in my opinion, I still needed to mock it for my unit tests.

    This PR includes Router matching of query parameters including wildcard values.

    Basically, this route registration is now possible:

    router.get("/users?language=:lng") { request in
        let language = request.components["lng"]
        ...
    }
    
    // send request to '.../users?language=xx'
    
    opened by Landsi 14
  • Allow `Data` as Serialized object even if is not valid json

    Allow `Data` as Serialized object even if is not valid json

    This enable Kakapo to stub other kind of requests, for example we can create a XMLSerialzier that when serialized returns the object as Data, or we can implementer Serialziable for UIImage, a really powerful feature that let us use Kakapo not only for JSON responses but every kind of response. This is a basic change that enable us to do so, some more documentation might be written in future and we might decide to move in a direction where Kakapo is less JSON centric

    Example:

    extension UIImage: ResponseFieldsProvider {
    
        public var statusCode: Int {
            return 200
        }
    
        public var body: Serializable {
            return UIImagePNGRepresentation(self)
        }
    
        public var headerFields: [String : String]? {
            return ["Content-Type": "image/png"]
        }
    }
    

    Usage:

    router.get("image.png") {
       return UIImage(named: "whatever")
    }
    
    opened by MP0w 11
  • Swift 2.2 support in 1.x release branch

    Swift 2.2 support in 1.x release branch

    Hi!

    Do you plan to support Swift 2.2 in 1.x release branch? There are just couple of lines that prevent 1.0.1 to be compatible with Swift 2.2.

    I could make such PR but I'm not sure which branch I should use, could you point me the right one?

    Thanks

    enhancement 
    opened by amarcadet 11
  • Update APIs to follow swift 3 naming guidelines

    Update APIs to follow swift 3 naming guidelines

    https://swift.org/blog/swift-3-api-design/

    • [x] Update public API (not private for the moment)
    • [x] Update example app and playground
    • [x] Update README code
    • [x] Write changelog with BCHs
    • [x] PR
    • [ ] Release 2.0.0 📦
    enhancement 
    opened by MP0w 11
  • Support Deletion and Overwrite in database

    Support Deletion and Overwrite in database

    Delete and update entities is not supported. As discussed we won't support:

    user.delete()
    user.save()
    

    This would require every entity to hold a database so extra setup for the user. The idea is to provide:

    db.delete(entity)
    db.save(entity) // or replace/createIfNeeded/.... [discussion]
    

    KakapoDB will have to find the entity, make sure that is equal (not only the id) to prevent removing another entity and/or using the wrong database.

    If an entity changes and is not saved into db delete will fail because doesn't match the entity in db.

    enhancement feature 
    opened by MP0w 10
  • Init entities and relationships

    Init entities and relationships

    Every entity that conform to the protocol (ATM called KakapoSerializable) will have to implement init(id: String). To create entities there might be 2 ways:

    db.create(20, User) //pseudocode
    // or
    db.create(20) { (id) in
        return User(name: Faker.name, friends: db.findRandom(Friends))
    }
    

    Friends is a relationship od User:

    struct Friend: KakapoModel {
        let id: String
        let name: String
        init(id: String) {
           self.id = id
           name = Faker.name
        }
    }
    

    User will init is relationship as:

    struct User: KakapoModel {
        let id: String
        let name: String
        let friends: [Friend]
        init(id: String) {
           self.id = id
           name = Faker.name
           friends = 1...(arc4random()).each{ Friend(id: NSUUID()) }
        }
    }
    
    discussion 
    opened by MP0w 9
  • Request info refactor

    Request info refactor

    public struct Request {
        let info: URLInfo
        let HTTPBody: NSData?
    }
    

    I don't find it easy to use info.params and info.queryParams.

    what about:

    public struct Request {
        let parameters: [String : String]
        let components: [String : String]
        let HTTPBody: NSData?
        let HTTPHeaders: [String: String]?
    }
    

    for /user/:id then we would have: components = ["id": "value"] parameters are the url parameters

    @devlucky/js-core ?

    enhancement discussion 
    opened by MP0w 8
  • Apply migration to Swift 4

    Apply migration to Swift 4

    Implements #158.

    Changelog

    Added Swift version file. Moved Swift version to Project instead of Targets. Updated dependencies. Fixed SwiftLint errors. Splitted Router tests between multiple files. Renamed some variables in tests.

    opened by Ethenyl 7
  • Access request body from route handler

    Access request body from route handler

    I think we should we able to provide access to the request body in the request handler:

    let server = KakapoServer()
    
    server.get("/user", (request) {
      return request.body
    }
    
    
    bug 
    opened by zzarcon 7
  • FIX type naming

    FIX type naming

    Proposing different naming, we should avoid prefixing as it's useless. I would maybe just leave it for too generic things like DB. But HTTPMethod should be okay especially because it is an enum inside a class (ClassName.HTTPMethod)

    @joanromano @zzarcon overall very cool job, I have some changes in mind for the DB but that will come after. Awesome. partyparrot

    opened by MP0w 6
  • Issue when using SwiftLint, carthage failed to build scheme

    Issue when using SwiftLint, carthage failed to build scheme

    When SwiftLint is installed it is not possible to use this library because it fails during build scheme step when doing Carthage update.

    Build failed: Task failed with exit code 65.

    When uninstalling SwiftLint, it builds with success.

    Do you need anything else to help you investigate/fix this issue ?

    Regards,

    opened by martiwi 5
  • Refactor Kakapo to not be json centric WIP

    Refactor Kakapo to not be json centric WIP

    This is a draft PR to address #153, I want some feedback before proceeding with updating the documentation, README, tests The idea is that Router now doesn’t work anymore w/ Serializable objects and ResponseFieldsProvider is not anymore a special Serializable. Router now only accept ResponseFieldsProviders an object that provides: statusCode (Int) headerfields (dict) body (data)

    Serializable became a ResponseFieldsProvider that by default has a status code == 200 and “Content-Type” == “application/json”. It should have been like that since ever, so we can use Kakapo with images, xml or whatever we want by creating new ResponseFieldsProvider

    The change is a breaking change but not a big one for users of Kakapo (only objects that were confirming to ResponseFieldsProvider will be broken)

    opinionated 
    opened by MP0w 5
  • Provide a

    Provide a "Request.continue()" that performs the original request

    As discussed here https://github.com/devlucky/Kakapo/pull/140#issuecomment-256661672 and here https://github.com/devlucky/Kakapo/pull/140#issuecomment-258459938

    This would let you fallback on the original response. I want to discuss to understand if we should add this to Kakapo or not.

    example

    router.get("/users/:id") { request in
      if wantToHandleIt {
         return something  
      }
      return request.continue()
    }
    

    API naming could also be Request.original() or something like that.

    discussion 
    opened by MP0w 0
Releases(1.1.1)
  • 1.1.1(Oct 18, 2016)

  • 2.1.0(Oct 14, 2016)

  • 1.1.0(Oct 14, 2016)

  • 2.0.0(Sep 30, 2016)

    Breaking

    • _Swift 3.0 Support_

    • Renamed HTTPBody to httpBody and HTTPHeader to httpHeader

    • HTTPMethod enum cases are now lowercase

    • Updated APIs to follow Swift 3 new naming guidelines:

      Serializable

      • serialize(_ keyTransformer: KeyTransformer? = nil) -> Any? -> serialized(transformingKeys keyTransformer: KeyTransformer? = nil) -> Any?

      CustomSerializable

      • customSerialize(_ keyTransformer: KeyTransformer?) -> Any? -> customSerialized(transformingKeys keyTransformer: KeyTransformer?) -> Any?

      Store

      • filter<T: Storable>(_: T.Type, includeElement: (T) -> Bool) -> [T] -> filter<T: Storable>(_: T.Type, isIncluded: (T) -> Bool) -> [T]

      JSONAPISerializable

      • data(includeRelationships: Bool, includeAttributes: Bool, keyTransformer: KeyTransformer?) -> Any? -> data(includingRelationships: Bool, includingAttributes: Bool, transformingKeys keyTransformer: KeyTransformer?) -> Any?
      • includedRelationships(includeChildren: Bool, keyTransformer: KeyTransformer?) -> [Any]? -> includedRelationships(includingChildren: Bool, transformingKeys keyTransformer: KeyTransformer?) -> [Any]?

      JSONAPISerializer

      • init(_ object: T, topLevelLinks: [String: JSONAPILink]? = nil, topLevelMeta: Serializable? = nil, includeChildren: Bool = false) -> init(_ object: T, topLevelLinks: [String: JSONAPILink]? = nil, topLevelMeta: Serializable? = nil, includingChildren: Bool = false)
    Source code(tar.gz)
    Source code(zip)
  • 1.0.1(Sep 26, 2016)

  • 1.0.0(Sep 24, 2016)

    • Swift 2.3 support
    • Renamed KakapoDB to Store
    • init(id:db:) required by Storable protocol has been changed to init(id:store:)
    • Renamed KakapoServer to Server
    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Sep 19, 2016)

    • Just another Swift 2.2 release before 1.0.0
    • Implement NSURLProtocol.stopLoading() for delayed requests (#96) by @leviathan
    • Prevent empty include array on `JSONAPISerializable arrays when no relationships
    • Update excluded link key topLinks to relationshipsLinks
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Aug 10, 2016)

  • 0.0.1-beta2(Jul 3, 2016)

  • 0.0.1-beta1(Jun 30, 2016)

  • 0.0.1-alpha4(Jun 16, 2016)

  • 0.0.1-alpha3(May 1, 2016)

    JSON API basic support Support for multiple Routers Support for baseURL in Routers Support for HTTPHeaders Routers can be unregistered Optional now won't be included in the JSON when nil ProperyPolicy is now an extended Optional with .Null support

    Source code(tar.gz)
    Source code(zip)
  • 0.0.1-alpha2(Apr 25, 2016)

    ADD Response HTTPHeader support ADD Response status code support ADD update delete entities from database ADD iOS 8 support Refactored request info

    Source code(tar.gz)
    Source code(zip)
  • 0.0.1-alpha1(Apr 25, 2016)

Mock Alamofire and URLSession requests without touching your code implementation

Mocker is a library written in Swift which makes it possible to mock data requests using a custom URLProtocol. Features Requirements Usage Activating

WeTransfer 898 Dec 26, 2022
MockSwift is a Mock library written in Swift.

Welcome to MockSwift MockSwift allows you to write mocks and make better tests. Because MockSwift is an open source library 100% written in Swift, it

Jordhan Leoture 82 Dec 17, 2022
A Popover that mock iOS SkinTone Selection Popover.

IMessage SkinTone Popover This is a popover mock the iOS iMessage Skin Tone Selection Menu (Popover) Features Long press to invoeke the popover, and t

Vincent Liu 1 Dec 9, 2021
Automatic testing of your Pull Requests on GitHub and BitBucket using Xcode Server. Keep your team productive and safe. Get up and running in minutes. @buildasaur

Buildasaur Automatic testing of your Pull Requests on GitHub and BitBucket using Xcode Server. Keep your team productive and safe. Get up and running

Buildasaurs 774 Dec 11, 2022
BDD Framework and test runner for Swift projects and playgrounds

Spectre Special Executive for Command-line Test Running and Execution. A behavior-driven development (BDD) framework and test runner for Swift project

Kyle Fuller 392 Jan 1, 2023
Implementing and testing In-App Purchases with StoreKit2 in Xcode 13, Swift 5.5 and iOS 15.

StoreHelper Demo Implementing and testing In-App Purchases with StoreKit2 in Xcode 13, Swift 5.5, iOS 15. See also In-App Purchases with Xcode 12 and

Russell Archer 192 Dec 17, 2022
Stub your network requests easily! Test your apps with fake network data and custom response time, response code and headers!

OHHTTPStubs OHHTTPStubs is a library designed to stub your network requests very easily. It can help you: test your apps with fake network data (stubb

Olivier Halligon 4.9k Dec 29, 2022
I built this application with unit testing and test-driven development to understand TDD theory and practice

TestDrivenDevelopment Description I built this application with unit testing and test-driven development to understand TDD theory and practice, to wri

null 1 Dec 21, 2021
A Matcher Framework for Swift and Objective-C

Nimble Use Nimble to express the expected outcomes of Swift or Objective-C expressions. Inspired by Cedar. // Swift expect(1 + 1).to(equal(2)) expect(

Quick 4.6k Dec 31, 2022
PinpointKit is an open-source iOS library in Swift that lets your testers and users send feedback with annotated screenshots using a simple gesture.

PinpointKit is an open-source iOS library in Swift that lets your testers and users send feedback with annotated screenshots using a simple gesture. F

Lickability 1.1k Jan 6, 2023
The Swift (and Objective-C) testing framework.

Quick is a behavior-driven development framework for Swift and Objective-C. Inspired by RSpec, Specta, and Ginkgo. // Swift import Quick import Nimbl

Quick 9.6k Dec 31, 2022
This repository accompanies Test-Driven Development in Swift: Compile Better Code with XCTest and TDD

Apress Source Code This repository accompanies Test-Driven Development in Swift: Compile Better Code with XCTest and TDD by Gio Lodi (Apress, 2021). D

Apress 57 Jan 1, 2023
Test task application based on Swift using CoreData, Alamofire, AlamofireImage and CocoaPods

iTunes Search Test task application based on Swift using CoreData, Alamofire, AlamofireImage and CocoaPods Features ?? Searching music albums by name

Alexander Zhukov 0 Oct 31, 2021
Lightweight touch visualization library in Swift. A single line of code and visualize your touches!

TouchVisualizer is a lightweight pure Swift implementation for visualising touches on the screen. Features Works with just a single line of code! Supp

Morita Naoki 851 Dec 17, 2022
Erik is an headless browser based on WebKit. An headless browser allow to run functional tests, to access and manipulate webpages using javascript.

Erik Erik is a headless browser based on WebKit and HTML parser Kanna. An headless browser allow to run functional tests, to access and manipulate web

Eric Marchand 544 Dec 30, 2022
View your app on different device and font sizes

Sizes reduces the time it takes to evaluate all of our apps possible device sizes, orientations and font combinations. With Sizes we'll avoid launchin

Marcos Griselli 1.2k Oct 27, 2022
Snapshot testing tool for iOS and tvOS

SnapshotTest is a simple view testing tool written completely in Swift to aid with development for Apple platforms. It's like unit testing for views.

Pär Strindevall 44 Sep 29, 2022
UI Testing Cheat Sheet and Examples.

UI Testing Cheat Sheet This repository is complementary code for my post, UI Testing Cheat Sheet and Examples. The post goes into more detail with exa

Joe Masilotti 2.1k Dec 25, 2022
Take home task from one company developed on VIPER, UI Through code and splash from plist

HelloFresh Dev Team - Mobile iOS Developer Test Hello! Thank you for taking the time to try our iOS test. The goal of the test is to assess your codin

Jawad Ali 4 Aug 13, 2022