Swiftpack.co - jemmons/Perfidy as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by jemmons.
jemmons/Perfidy v6.0.0
Mock service for iOS testing.
⭐️ 0
🕓 4 weeks ago
.package(url: "https://github.com/jemmons/Perfidy.git", from: "v6.0.0")

Perfidy

Fake service for iOS testing.

SwiftPM compatible

Perfidy is tiny HTTP server implemented with SwiftNIO that you can spin up in your tests to fake out your API without hitting the network. This keeps your tests isolated and fast.

The syntax is, at least initially, pretty straight forward:

import Perfidy
let server = FakeServer()
server.start()
//...
server.stop()

Though the whole point is to run it in a test. Here’s what it looks like in an XCTestCase:

import XCTest
import Perfidy
import YourApp


class MessageTests: XCTestCase {
  // By default the fake server will listen to `localhost` on port `10175`.
  let server = FakeServer()
  
  override func tearDown() {
    // To maintain, isolation disconnect the server after every test.
    server.stop()
  }
  
  // Pretend we want to test an app that will update a mesasge...
  func testPostData() {
    // Network tests are, by their nature, async. Expectations are our friends!
    let expectUpdateRequestSent = expectation(description: "Should send a request to update the message.")
    let expectConfirmation = expectation(description: "Should display update confirmation.")
    
    // Add an endpoint to our fake server with the response we expect and a 
    // closure that gets called whenever a request is received.
    //
    // Our response here is a simple status code, but we can also specify that
    // JSON or arbitrary `Data` get returned in the body.
    server.add("PUT /message/1", response: 204) { request in
      // Verify the expected properties have been sent by the app here. Then:
      expectUpdateRequestSent.fulfill()
    }
    
    // Start the fake server. From now until `server.stop()` it’s listening for connections.
    try! server.start()
    
    // Do what we have to to make our app update a message. This will be “heard”
    // by our fake server, which will respond according to the endpoint we added
    // up above.
    MyApp.shared.updateMessage(title: "New Title")
    
    // Then check the app updated its UI after receiving the server response.
    // `NotificationCenter` can be useful for this.
    expectConfirmation.fulfill()
    
    wait(for: [expectUpdateRequestSent, expectConfirmation], timeout: 1)
  }
}

If you, like me, kinda hate tearDown, there’s also a trailing-closure version:

class MessageTests: XCTestCase {
  func testPostData() {
    let expectUpdateRequestSent = expectation(description: "Should send a request to update the message.")
    let expectConfirmation = expectation(description: "Should display update confirmation.")

    FakeServer.runWith { server in
      // The server is already running at this point. But we can still add endpoints.
      server.add("PUT /message/1", response: 204) { request in
        expectUpdateRequestSent.fulfill()
      }

      MyApp.shared.updateMessage(title: "New Title")
      
      // ...we get a notification from the app or something on update, then:
      expectConfirmation.fulfill()
      
      wait(for: [expectUpdateRequestSent, expectConfirmation], timeout: 1)
      
      // No need to call `stop` on the server. It stops on its own when leaving this scope. 
    }
  }

And there’s a lot more I haven’t documented yet 😰. Stuff like pointing to a single JSON file to load a whole bunch of endpoints with full responses in one go. If you have questions, don’t hesitate to reach out for more details. It’s my fault for not writing faster.

GitHub

link
Stars: 0
Last commit: 4 weeks ago
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.

Dependencies

Release Notes

Calls Route handlers from main thread.
3 years ago

Previously Perfidy was calling any Route handlers from the thread of the NIO event loop that triggered them. But these handlers are, more often than not, closures created in tests that interacted with UI elements. So it’s exceedingly rare to see one that doesn’t immediately jump to DispatchQueue.main — or should and forgets to do so.

So Perfidy does this for us now. All Route handlers get DispatchQueue.main.async’d to the main thread.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics