Reputation: 1421
I am working on testing an API through Alamofire. I need to make a single call to the server to prepare it for the integration test. Once that is done, I am ready to start running tests.
The usual override setUp()
is run for every test, so I do not want to do that.
I have therefore chosen to override the class setUp()
as described here: https://developer.apple.com/reference/xctest/xctestcase
That's all well and good, but now, I no longer can use the standard waitForExpectations
. (In the class override setUp())
I get several compiler errors that tell me that I am no longer calling the same waitForExpectations
because I am in a class method, not a test case.
To try to get around this, I wanted to use a semaphore like so:
class ServiceLayerTests: XCTestCase {
static var apiService: APIService = APIService()
let sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: ["http://localhost:3000/": .disableEvaluation]))
static var companyManger: UserWebResource?
static var companyManagerID = -1
override class func setUp() {
apiService.baseURL = "http://localhost:3000/"
beginIntegrationTests()
}
class func beginIntegrationTests() {
var semaphore = DispatchSemaphore(value: 0)
apiService.beginIntegrationTests(completion: {resource, error in
if let resource = resource {
if let manager = resource as? UserWebResource {
companyManger = manager
companyManagerID = manager.id
semaphore.signal()
}
}
})
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
}
}
This does not work. Under the hood, there is an alamo fire call to the server and it responds with the user to use for the integration tests. I do see the server spinning, so I know that the actual communication is happening, but I never get into the completion closure.
I suspect I am not understanding how Swift does semaphores and that I have created a deadlock somehow. If somebody has a better solution, I'd be more than happy to hear it.
Upvotes: 4
Views: 972
Reputation: 4531
You can create the expectation as a lazy var that executes your one-time set up and fulfills on completion.
At the beginning of your per-test setUp()
function you can wait for that expectation.
None of your tests will run until it is fulfilled, and the initial setup will run only once.
class WaitForSetup_Tests: XCTestCase {
lazy var initialSetupFinished: XCTestExpectation = {
let initialSetupFinished = expectation(description: "initial setup finished")
initialSetupTask() { // your setup task that executes this closure on completion
initialSetupFinished.fulfill()
return
}
return initialSetupFinished
}()
override func setUp() {
wait(for: [initialSignOutFinished], timeout: 2.0)
// do your per-test setup here
}
}
Note: This solution avoids using the override class function setUp()
class method, because I couldn't figure out how to use the expectations except for in an instance.
Upvotes: 0
Reputation: 4946
I get several compiler errors that tell me that I am no longer calling the same
waitForExpectations
because I am in a class method, not a test case
That makes sense. What you probably want is to refactor so that you are in a test case:
override class func setUp() {
apiService.baseURL = "http://localhost:3000/"
}
func testIntegrationTests() {
let urlExpectation = expectation(description: "INTEGRATION TEST")
apiService.beginIntegrationTests(completion: {resource, error in
// ...
urlExpectation.fulfill()
})
// not sure what an acceptable timeout would be, I chose this at random
waitForExpectations(timeout: 25) { error in
if let error = error {
print("Error: \(error.localizedDescription)")
}
}
}
One of the best resources with some good test examples can be found here: http://nshipster.com/xctestcase/
Upvotes: 1