Configuring Vapor apps for Testing
When you create a new Vapor application using the command line tools the framework provides, there’s no test target created by default. So, what do you need to do to add tests to your new app?
This guide will help you set up your Vapor project for testing.
TL;DR
I’ve created an alternative template for Vapor already set up for testing. If you’re creating a new project you can use the following command to kick start a project with testing in mind:
vapor new MyApp --template=https://github.com/gtranchedone/vapor-testing-template
N.B. The executable name given by this template is called Executable
not App
, like the default template! You may need to change some of your deployment configurations. See the notes at the end of this article to see how to update your Heroku configuration.
If you already have a project set up and you want to start writing tests for it, you’ll have to follow the instructions below.
1. Prepare the app for testing
In my last article I’ve discussed how the Swift Package Manager works, and that is fundamental to understanding how to structure your project for testing.
In a nutshell, every subdirectory in the Sources
folder is considered to be a target. The same applies to the subdirectories in the Tests
folder. Also, any folder containing a main.swift
file is considered an executable.
Vapor’s default template creates the following folder structure (I’m omitting some files for the sake of brevity):
.
├── Config
├── Localization
├── Package.swift
├── Public
│ ├── images
│ └── styles
├── README.md
├── Resources
│ └── Views
├── Sources
│ └── App
│ ├── Controllers
│ │ └── PostController.swift
│ ├── Models
│ │ └── Post.swift
│ └── main.swift
└── app.json
Vapor creates an App
folder that is an executable. Unfortunately, at the time being XCTest
doesn’t support testing executables. Therefore, the first step we have to take is make our App a library so that XCTest
can run properly.
Let’s move the main.swift
file from Sources/App
to a new folder Sources/Executable
. You can call this folder whatever you like; it’s not important.
As the entry point to the app now lives in a new module, you’ll need to import the App
module to use its classes for routing. Add the following line to main.swift
:
import App
Finally, we have to declare that our package now has an Executable
target and that it depends on the App
module. Update the package manifesto, Package.swift
, with the following targets
property:
Note that the exclude
property should not contain the entry Tests
, which was present, until recently, in Vapor’s default template.
The new folder structure now should look like this:
.
├── Config
├── Localization
├── Package.swift
├── Public
│ ├── images
│ └── styles
├── README.md
├── Resources
│ └── Views
├── Sources
│ ├── App
│ │ ├── Controllers
│ │ │ └── PostsController.swift
│ │ └── Models
│ │ └── Post.swift
│ └── Executable
│ └── main.swift
└── app.json
2. Add a test target
Now that the app is properly divided into executable and core app domain, you can add a test target and tests to the project.
To start with, create a Tests
directory at the root of your repo. Inside, add a folder called AppTests
.
I like to have the same folder structure for both the App
and AppTests
directories. Therefore, I’ll add both a Controllers
and a Models
folders into AppTests
. Finally, let’s add some tests. Here’s an example:
Now, your app should look like this:
.
├── Config
├── Localization
├── Package.swift
├── Public
│ ├── images
│ └── styles
├── README.md
├── Resources
│ └── Views
├── Sources
│ ├── App
│ │ ├── Controllers
│ │ │ └── PostsController.swift
│ │ └── Models
│ │ └── Post.swift
│ └── Executable
│ └── main.swift
├── Tests
│ └── AppTests
│ ├── Controllers
│ │ └── PostsControllerTests.swift
│ └── Models
│ └── PostTests.swift
└── app.json
3. Fix the tests on Linux
There’s one last problem we have to fix. When running the tests on a Mac, XCTest
currently uses the Objective-C runtime to find your tests. However, as there’s no way to do this with Swift, when running the tests on Linux, for example on a CI server, your tests won’t be found, and you’ll get an error.
To fix this issue, you need to add a LinuxMain.swift
file inside the Tests
directory. As described in the documentation of XCTest
, this file needs to explicitly call a function of the framework with the tests you want to run.
The recommended way to handle this is to create a static method in each of your test cases and call it from the LinuxMain.swift
file.
With this configuration in place, your tests will run perfectly fine on both macOS and Linux. So, your final application will look like this:
.
├── Config
├── Localization
├── Package.swift
├── Public
│ ├── images
│ └── styles
├── README.md
├── Resources
│ └── Views
├── Sources
│ ├── App
│ │ ├── Controllers
│ │ │ └── PostsController.swift
│ │ └── Models
│ │ └── Post.swift
│ └── Executable
│ └── main.swift
├── Tests
│ ├── AppTests
│ │ ├── Controllers
│ │ │ └── PostsControllerTests.swift
│ │ └── Models
│ │ └── PostTests.swift
│ └── LinuxMain.swift
└── app.json
Notes
This template has an important implication on how your app is executed on the server. In particular you want to make sure that the right executable is being run on the server. By default, Vapor and all tutorials out there assume your executable name is called “App”, however, the executable in our case is called “Executable”.
Updating the Heroku configuration
If you’re using Heroku to deploy your app, edit the Procfile and change the first occurrence of App
to Executable
. (Note that the following should be all on one line.)
web: Executable --env=production --workdir=./ --config:servers.default.port=$PORT
When you’re setting up Heroku with Vapor using the Vapor Toolbox, you’ll be asked if you want to use a custom executable name. At that point, you should answer ‘yes’ and type ‘Executable’ as the given name. That will make your Procfile right on configuration.
vapor heroku init
...
Do you want to use a custom executable name?
y/n? y
Executable name > Executable
...
Don’t repeat yourself: use a template!
I’ve created a template you can use when creating new Vapor apps using the vapor toolbox, like so:
vapor new MyApp -template=https://github.com/gtranchedone/vapor-testing-template
This will create a project with the folder structure and configuration described in this article, plus an initial test for routes.
Subscribe to Swift Web Weekly!
If you’re interested in web development with Swift, Swift Web Weekly is a free newsletter I’m curating with interesting links related to server-side Swift, HTML, CSS, JavaScript, tools and more. If you don’t like it you can unsubscribe anytime, so why don’t give it a try?! 🙂