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
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.
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 validXCUITestContextScope
associated with theXCUITestContext
that is connected toself
(XCTestCase
). You can see whether there's a valid scope by typingpo [[self testContext] currentScope]
in LLDB while inside anXCTestCase
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 onXCTestCase
but calls the (designated initializer?)-initWithInvocation:
withnil
. This happens before a scope is set (usually via-[XCUITestContext performInScope:]
) so the scope isnil
). Later on, new instances of our spec class are created with-initWithInvocation:
with our invocations we created per example. However, since our code referredself
we captured the dummy instance of theXCTestCase
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 theXCTestCase
at hand.