Skip to content

Calling waitUntil in a QuickConfiguration times out for AsyncSpec #1126

@CraigSiemens

Description

@CraigSiemens
  • I have read CONTRIBUTING and have done my best to follow them.

What did you do?

If a QuickConfiguration contains a call to waitUntil, it will always timeout when running an AsyncSpec. In our case, we're using beforeSuite and afterSuite to call asynchronous methods to save and restore the values in the keychain to avoid

Below is the simplest way I could find to reproduce it. The test will always fail with the message

class AsyncConfiguration: QuickConfiguration {
    override class func configure(_ configuration: QCKConfiguration) {
        configuration.beforeSuite {
            waitUntil { done in
                done()
            }
        }
    }
}

class AsyncConfigurationSpec: AsyncSpec {
    override class func spec() {
        it("should be called") {}
    }
}

What did you expect to happen?

waitUntil doesn't timeout when the test is a subclass of QuickSpec.

What actually happened instead?

The closure passed to waitUntil is never called. Neither a breakpoint or a print are called until after waitUntil has timed out.

Increasing the timeout interval has no affect. The test will always wait the full timeout before failing.

The test failed with the following message.

should be called(): Waited more than 1.0 second

Environment

List the software versions you're using:

  • Quick: 7.4.0
  • Nimble: 13.2.0
  • Xcode Version: 15.2
  • Swift Version: 5.9

Please also mention which package manager you used and its version. Delete the
other package managers in this list:

  • Swift Package Manager - Swift 5.9.0

Project that demonstrates the issue

I reproduced it with the Quick repo, since I wasn't sure which repo was the cause of the issue.

  1. Clone Quick
  2. Copy the following diff
  3. pbpaste | git apply
  4. Open Quick and run the tests.
diff --git a/Package.swift b/Package.swift
index 264a133..a268321 100644
--- a/Package.swift
+++ b/Package.swift
@@ -35,6 +35,10 @@ let package = Package(
                 name: "QuickIssue853RegressionTests",
                 dependencies: [ "Quick" ]
             ),
+            .testTarget(
+                name: "QuickAsyncConfigurationTests",
+                dependencies: [ "Quick", "Nimble" ]
+            ),
         ]
 #if os(macOS)
         targets.append(contentsOf: [
diff --git a/[email protected] b/[email protected]
index 4ede907..5daa938 100644
--- a/[email protected]
+++ b/[email protected]
@@ -35,6 +35,10 @@ let package = Package(
                 name: "QuickIssue853RegressionTests",
                 dependencies: [ "Quick" ]
             ),
+            .testTarget(
+                name: "QuickAsyncConfigurationTests",
+                dependencies: [ "Quick", "Nimble" ]
+            ),
         ]
 #if os(macOS)
         targets.append(contentsOf: [
diff --git a/Tests/QuickAsyncConfigurationTests/AsyncConfigurationSpec.swift b/Tests/QuickAsyncConfigurationTests/AsyncConfigurationSpec.swift
index ea0cc4b..2cfa8a0 100644
--- a/Tests/QuickAsyncConfigurationTests/AsyncConfigurationSpec.swift
+++ b/Tests/QuickAsyncConfigurationTests/AsyncConfigurationSpec.swift
@@ -2,30 +2,18 @@ import XCTest
 import Quick
 import Nimble
 
-class AsyncConfigurationSpec: AsyncSpec {
-    override class func spec() {
-        beforeEach {
-            FunctionalTests_Configuration_AfterEachWasExecuted = false
-        }
-        it("is executed before the configuration afterEach") {
-            expect(FunctionalTests_Configuration_AfterEachWasExecuted).to(beFalsy())
+class AsyncConfiguration: QuickConfiguration {
+    override class func configure(_ configuration: QCKConfiguration) {
+        configuration.beforeEach {
+            waitUntil { done in
+                done()
+            }
         }
     }
 }
 
-final class Configuration_AfterEachAsyncTests: XCTestCase, XCTestCaseProvider {
-    static var allTests: [(String, (Configuration_AfterEachAsyncTests) -> () throws -> Void)] {
-        return [
-            ("testExampleIsRunAfterTheConfigurationBeforeEachIsExecuted", testExampleIsRunAfterTheConfigurationBeforeEachIsExecuted),
-        ]
-    }
-
-    func testExampleIsRunAfterTheConfigurationBeforeEachIsExecuted() {
-        FunctionalTests_Configuration_AfterEachWasExecuted = false
-
-        qck_runSpec(Configuration_AfterEachAsyncSpec.self)
-        XCTAssert(FunctionalTests_Configuration_AfterEachWasExecuted)
-
-        FunctionalTests_Configuration_AfterEachWasExecuted = false
+class AsyncConfigurationSpec: AsyncSpec {
+    override class func spec() {
+        it("should be called") {}
     }
 }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions