1. 程式人生 > >Building an Excellent Mock Server in Swift using Vapor

Building an Excellent Mock Server in Swift using Vapor

What is a Mock Server?

Before explaining what a mock server is, we’ll start with what a mock is and why you might use one. I’ll also avoid delving into the differences between a mock, a stub, and a fake. Just know that they’re all almost the same. Although there have been numerous articles written on the subject (

for example) a general consensus among professionals is still hard to reach.

Generally, a mock, and to some extent a stub/fake is just some part of your code you need to substitute for testing purposes. The distinction I like is that a mock in and of itself does not have a predefined behavior.

The mock server I’ll share here is meant to act as a stand-in for any HTTP or HTTPS operations for your application under test.

I think having a server like this works well at the component or integration level of testing where you may have one or more different services that your code depends on which are not practical to make requests to while testing. While using a stub could achieve similar results, the use of a mock server will allow you to fully exercise any HTTP client code you may be using.

The Code

Conveniently, when you create a new vapor API server as we did earlier, vapor creates a simple hello, world and todo app within your new application. While we’ll end up deleting most of this code, it is helpful to see how a simple MVC is structured using vapor. Since this example application also goes through the trouble of bootstrapping a SQLite database we’ll go ahead and use that to store our mock responses. Under normal circumstances I would just recommend storing these as a dictionary in memory, but since the database is already wired up we may as well use it.

MockResponse Model

The MockResponse will be the primary object type we’re dealing with in our mock server. This class will expand on the SQLiteModel class which will allow us to use the convenient built-in methods for storage and retrieval of records.

MockResponse will include several properties including a unique ID, associated route, http method, response code, custom headers, and finally a payload.

Because this is meant to be a generic mock server it should be able to support a variety of different payload formats. Swift uses a protocol named Codable for encoding/decoding JSON. Unfortunately, Codable does not support dynamic dictionary structures, so I turned to a library named Codability which provides a class named AnyCodable that will allows for arbitrary dictionary structures to conform to the Codable protocol without having to be defined prior.

MockController

The MockController will act as the controller in our application. For simplicity it lacks the ability to update existing mock records and only handles record creation, deletion, listing, and most importantly retrieval and response formatting.

Router

routes.swift handles routing within the vapor app and extends the Vapor Router class.

router.swift includes static routes for mock creation, listing, and deletion, as well as dynamic routes to support any mock endpoints you may end up adding. In order to fully support any url path I needed to rely on PathComponent.catchall, or just “all” as it can be accessed. This is a bit of a break from standard vapor routing and is really intended to serve as a generic request handler, which means some of the normal routing support paradigms aren’t available, like parameter handling.

I would NOT recommend using the all parameter for any kind of production service. It’s just opening yourself up to potential usability and security issues.

ResponseMapper

I ended up writing a new class to contain my utilities methods so I wouldn’t need to clutter my model or controller classes. These ResponseMapper functions are generally used for preparing the mock response by deciphering the AnyCodable payload attribute on the MockResponse object and using those details to produce the correct response body and headers.

Running the Server

You can build and run your vapor app from the command line by running.

$ vapor build && vapor run

You can also open the application in XCode and run there.

$ vapor xcode

The Takeaway

Getting started with Vapor 3 was really a breeze. While I haven’t checked out its competitors yet, if you’re goal is to build a simple MVC application I would give it a shot.

The only issues I encountered during this project were finding accurate documentation (some things have changed since Vapor 2 and the documentation does not seem quite complete) and learning more about Swift’s Codable protocol and using Codability to conform to it.

Overall I would recommend Vapor and look forward to playing with it more in the future.

You can find my full code here.