Unit Tests in Server-Side Swift API
This post is originally appeared in my personal blog, available at candostdagdeviren.com
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.swiftfile is the main executable for app.
LinuxMain.swiftfile 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.swiftfile must be under the
- Create a static variable inside test classes and use it as a parameter while calling
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
OK, we have 2 different modules as
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
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.