Unit Tests in Server-Side Swift API

Read this post in a good format on Swift Post.

I’ve shared my first experiences about backend development in Swift in my previous blog post. This is the follow-up post. I’ll talk about Unit Tests in Swift.


  • Swift accepts only one executable file for the app. We need to split app and library to make it testable. Because testing requires the separate executable file.
  • main.swift file is the main executable for app. LinuxMain.swift file is the main executable for tests.
  • To split them, we’ll create two targets. One target is the library which will consist of all our code. The other one is the app which will be dependent on the library.
  • Do not forget to add ~Tests suffix for your test folders and test files.
  • LinuxMain.swift file must be under the Tests directory.
  • Create a static variable inside test classes and use it as a parameter while calling testCase() method in LinuxMain.swift file.

Restructuring the Project

After the first post, we should have executable Swift Backend API. In this post, we’ll restructure and reconfigure our beloved tiny module for tests and Dockerize it. First, one question comes to our minds: “Why we need to reshape our module to make it testable?”. Because we have the executable module right now (which is the API) and Swift doesn’t accept more than one main executable. At this point, to execute tests, we need a separate executable .swift file for tests. From this point, we’ll move main.swift as separate module and call it app. The logic and all other stuff will be in our library module. Here is the latest folder structure:

There are some points that we need to handle while separating our app and library. First, top-level expressions are not allowed except for main.swift file. It means that we can call methods or expressions outside of class, struct, enum etc. only in main.swift file like our current version:

After separating our Lib and App, we don’t have access to imported modules (in this case Kitura, HeliumLogger, and CouchDB) and custom created data structures (like MainRouter, DatabaseInteraction, etc). So, we’ll move all this code into a function in a new data structure (class or struct). In this case, it’s good practice to name this file same as our module, SwiftBackend.swift. After moving all code inside the main.swift file, our SwiftBackend.swift file becomes like this:

Now, we all need to do is creating this SwiftBackend class inside our main.swift file in the App module and call the method run. Just don't forget to import SwiftBackendLib to access SwiftBackend class. Here is the latest version of main.swift:

OK, we have 2 different modules as SwiftBackendLib and SwiftBackendApp. Lastly, we need to create these modules as targets in our Package.swift file and add our library as a dependency for app target. At the end, it should be like this:

We’re ready to start writing tests.

Creating & Running Unit Tests

Our restructuring is done. We can start testing. Mainly, there are few rules for tests. As you can see our folder structure above, inside Test folder, we have SwiftBackendLibTests. The first thing here is, whenever you create a folder under Test directory, you have to add ~Tests suffix to you folder name. Second, LinuxMain.swift file is the executable file for Tests. This is the main reason why we have to separate the library and the app in the first place. So, we have to place LinuxMain.swift file directly under the Tests directory. Finally, we can start writing some tests. Here is the example of basic test file:

As you can see we have allTests variable which is static. This is the way you can call tests from your LinuxMain.swift file. As you can see in LinuxMain.swift:

We’re using this static variable as a testCase parameter. This will run all tests inside the test file. So, when you add new test methods, do not forget to include them in the static variable return state. When you create new test classes, do not forget to add ~Tests suffix and add testCase method to LinuxMain.swift file with static variable parameter.


I’ll not explain how to setup Docker. There are great guides which explain everything in steps. I assume you already have running Docker Machine. Here is the basic Dockerfile to run this API with using Docker. It means that you can deploy almost anywhere you want (if supports Docker).

Let’s see what is going on this Dockerfile. We’re using ibmcom/swift-ubuntu:latest image as a base image. This will fulfill our needs while running. If there is no setup for Swift, it'll install required Linux packages to make it running. After that, we copy all of our current directory (all files) to under /app directory and making /app directory as our working directory. EXPOSE 8090 command exposes 8090 port for our app. So our app will be accessible via port number 8090. Then, we set our current user as a root user to run everything with root access. Finally, everything is ready and we can build and run our server API. RUN swift build runs the swift build command and it creates executable files. Last line of Dockerfile just executes this executable.

NOTE: In here we’re using debug executable. If you want to use release configuration, you can change RUN command to swift build -c release. This will create executable for release configuration. Then, you can use command .build/release/SwiftBackendApp in the last line.

You can run the API executable in your local with command docker run -p 8090:8090 swiftbackend. This should start running and you can start sending requests to your Docker Machine


Here we go. Finally, we have working Swift Server API. I tried to explain all steps I did. I spent a lot of time to make this app testable. I didn’t know that folder structure and naming really matters for running tests with Swift. If you had some troubles that I didn’t mention here, please add them as comments.

If you liked it, please click little ❤️ below to recommend it to your friends. If you want to be updated about new blog posts, you can go to my personal website and subscribe to my mail list. You can follow me on Twitter and GitHub.