Quick: Internal error: no current scope to add handler Notifications Dialog to

Created on 5 Apr 2016  路  8Comments  路  Source: Quick/Quick

Using it closure

it("should show main screen") { 
          self.addUIInterruptionMonitorWithDescription("Notifications Dialog") { (alert) -> Bool in
            alert.collectionViews.buttons.elementBoundByIndex(0).tap()
            return true
          }
}

I get "Internal error: no current scope to add handler Notifications Dialog to" when this main screen displays

enhancement help wanted feature

Most helpful comment

We've encountered the same issue in Specta, so I've done some digging that's relevant to this issue as well.

The -addUIInterruptionMonitorWithDescription: method expects that there will be a valid XCUITestContextScope associated with the XCUITestContext that is connected to self (XCTestCase). You can see whether there's a valid scope by typing po [[self testContext] currentScope] in LLDB while inside an XCTestCase class.

The issue stems from the fact that in order to create the test suite we initialize a new QuickSpec using -new and then run the -spec method on it (reference). The default -init initializer is not documented on XCTestCase but calls the (designated initializer?) -initWithInvocation: with nil. This happens before a scope is set (usually via -[XCUITestContext performInScope:]) so the scope is nil). Later on, new instances of our spec class are created with -initWithInvocation: with our invocations we created per example. However, since our code referred self we captured the dummy instance of the XCTestCase that had no scope, and we get a crash.

To summarize, referencing self in test cases is broken where there's an additional required state attached to the XCTestCase at hand.

All 8 comments

We are having the same issue - we are currently have a UI Testing target and are trying to handle System Alert Dialogs, specifically, like so: like so.

Here's our spec

import Quick
import Nimble

class HomeViewControllerUISpec: QuickSpec {
    override func spec() {
        var app: XCUIApplication!

        beforeEach {
            self.continueAfterFailure = false
            app = XCUIApplication()
            app.launch()
            app.tap()
            app.buttons["Go To Home"].tap()
        }

        describe("tapping Locate button") {
            it("should prompt user to use location services and after tapping 'OK' will fill zip code with 94102") {
                self.addUIInterruptionMonitorWithDescription("Location Dialog") {
                    (alert) -> Bool in
                    alert.buttons["Allow"].tap()
                    return true
                }

                app.buttons["Locate"].tap()
                app.tap()

                expect(app.staticTexts["94102"].exists) == true
            }
        }
    }
}

I get the following error

HomeViewController__tapping_Locate_button__should_prompt_user_to_use_location_services_and_after_tapping__OK__will_fill_zip_code_with_94102, 
failed: caught "NSInternalInconsistencyException", 
"Internal error: no current scope to add handler Location Dialog to."

Thanks for the reports. I've been meaning to look into XCUITest and Quick more, and this seems like a good place to start. In the meantime, please post here if you discover any workarounds. Also, if you could link me to some more information on what "context" refers to here, that'd be great.

This might also be worth filing a radar over. I assume Apple doesn't intend for XCTest to throw an exception in cases such as these -- or at least they should produce an error message that indicates what we're doing wrong.

@modocache - for what it's worth, we did try the same test, but in an XCTestCase and everything works fine.

import XCTest

class MapsUITests: XCTestCase {
    var app: XCUIApplication!

    override func setUp() {
        super.setUp()
        app = XCUIApplication()

        continueAfterFailure = false
        app.launch()
    }

    func test_locateButtonTapped_AllowButtonTapped_ZipCodeTextFieldShouldBe94102() {
        addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
            alert.buttons["Allow"].tap()
            return true
        }

        app.buttons["Locate"].tap()
        app.tap()
        XCTAssertTrue(app.staticTexts["94102"].exists)
    }
}

Currently, the only work around is to use an XCTestCase rather than a QuickSpec.

We've encountered the same issue in Specta, so I've done some digging that's relevant to this issue as well.

The -addUIInterruptionMonitorWithDescription: method expects that there will be a valid XCUITestContextScope associated with the XCUITestContext that is connected to self (XCTestCase). You can see whether there's a valid scope by typing po [[self testContext] currentScope] in LLDB while inside an XCTestCase class.

The issue stems from the fact that in order to create the test suite we initialize a new QuickSpec using -new and then run the -spec method on it (reference). The default -init initializer is not documented on XCTestCase but calls the (designated initializer?) -initWithInvocation: with nil. This happens before a scope is set (usually via -[XCUITestContext performInScope:]) so the scope is nil). Later on, new instances of our spec class are created with -initWithInvocation: with our invocations we created per example. However, since our code referred self we captured the dummy instance of the XCTestCase that had no scope, and we get a crash.

To summarize, referencing self in test cases is broken where there's an additional required state attached to the XCTestCase at hand.

Thanks for the great write-up! I wonder if this can be worked around somehow now that #645 has been merged? Maybe we can update the captured references somehow?

The only way to update the captured reference, AFAIK, is to mess with the blocks after their creation which involves knowledge of the blocks ABI, so I'm not sure this is the way we want to go.

I gave it some thought, and unless I'm missing something, I can't find a good reason for a spec to be a subclass of XCTestCase, as it's only used for the DSL which in turn dynamically adds - (void)test... methods to that class.

My suggestion is to avoid the confusion with self and -current by splitting it to two: the spec itself should create an NSObject that runs the current -spec method on -load or on demand. The spec will dynamically create an XCTestCase subclass and add the desired methods. Additionally, the generated class will have a testCase property that will return the current XCTestCase, similar to -current.

Note that with this approach, the API is more robust since you cannot call XCTestCase methods on self. It is also possible to add the desired XCTestCase methods to this object class as proxies to -testCase for ease of use.

Would be happy to hear your thoughts.

Hi, I also encountered this issue when using the latest version of Quick, should I use XCTest for this case or do you have any suggestion? Thanks.

Please use QuickSpec.current instead of self for now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

noisypigeon picture noisypigeon  路  6Comments

modocache picture modocache  路  7Comments

AndrewSB picture AndrewSB  路  3Comments

Jtango18 picture Jtango18  路  7Comments

michaelmcguire picture michaelmcguire  路  6Comments