vapor/api-template

Tests Folder

Closed this issue · 6 comments

I think it would be a good idea to encourage best practices by including a Tests folder in the Vapor project template.

I agree. However, in order to test the app you need to move the source code from an executable module into a library module. This makes things a little more complicated for people first starting out.

We need to find a way to keep the structure simple and allow testing.

Is the problem related to this Swift bug? https://bugs.swift.org/browse/SR-1503

Mmm, I wouldn't call that a bug. It's just that you can't import an executable module.

The situation here is exactly like it is with C/C++ – if you want to be able to dynamically share/load a body of code it needs to be put into a library, not an executable.

Luckily putting things you want to test in a library module is super easy. Even putting the Droplet there is do-able. You just have to wrap it in a function.

Something like:

module: MyLibrary

func makeDroplet() -> Droplet {
    let drop = Droplet()

    drop.get("foo") { ... }

    return drop
}

module: MyExecutable

import MyLibrary

let drop = makeDroplet()

drop.run()

module: MyTests

import MyLibrary
import XCTest

func testWhatever() {
    let drop = makeDroplet()
    try background { drop.run() } // you can run in background
    try dorp.respond(to: request) // or you can request directly
}

Tons of ways to do it.

The Swift issue I linked to is a genuine bug; on Linux, tests can't run at all for modules that contain a main.swift. (This works just fine on macOS.) It turns out, however, that that bug is only tangentially related to the executable vs. library issue that you mentioned.

The question remains, though, as to whether or not the template in this repo can or should provide a better starting place for Vapor users wanting to run tests on their Vapor projects. After noodling with my own project for a few days, I think it can.

First, I think it makes sense to adopt the workaround you mentioned as the standard way to do things in Vapor.

new file Sources/App/App.swift:

import Vapor

func makeDroplet() -> Droplet {
    let drop = Droplet()

    drop.get() { ... }

    return drop
}

Sources/App/main.swift:

let drop = makeDroplet()
drop.run()

Second, add a Tests folder and remove "Tests" from the excluded list in Package.swift.

Tests/LinuxMain.swift:

#if os(Linux)

import XCTest
@testable import AppTests

XCTMain([
    testCase(PostTests.allTests),
])

#endif

Tests/AppTests/PostTests.swift:

import XCTest
@testable import App

class PostTests: XCTestCase {
    static let allTests = [
        ("testFoo", testFoo),
    ]

    func testFoo() throws {
        XCTAssert("Foo" == "Foo")
    }
}

Third, and perhaps a little controversially, have vapor test move or rename Sources/App/main.swift temporarily while tests are running on Linux only. As a workaround for the bug in Swift.

That, I think, should make Vapor projects based on this template testable out of the box.

If you are interested in pursuing something like that, let me know and I'll put a pull request together.

I have a suggested approach that works nicely with Xcode.

It requires adding a new module under Sources (AppLogic is my suggested name).

App ends up just containing main.swift that imports AppLogic and then calls a function in there (configureDroplet() is my suggestion).

The creation of Models, Controllers, etc, are all then put into AppLogic. The current scripts all work with this structure, with the need for one small modification to the Package.swift. I needs to provide a Target description, like below:

import PackageDescription

let package = Package(
name: "GotYourBackServer",
targets: [
Target(name: "App", dependencies: ["AppLogic"]),
],
dependencies: [

And then also remove Tests from the exclude section