Xcode UI Testing: Automatically taking snapshot when predicates fail? - xcode

Xcode UI Testing takes automatic screenshots for viewing in the results navigator whenever a test fails, which is greatly helpful. However, that does not include tests that fail because a predicate is failed. Since predicates are often for basic checks (such as if an element exists or not on a current view), that is a huge drawback because a screenshot would be useful in diagnosing what was happening in the app when the test failed.
Does anyone know how to force a screenshot? Does this require integrating the Fastlane Snapshot tool?

On tearDown you can check if test failed (that's helpful if you are not discarding screenshots when tests pass.)
if let failureCount = testRun?.failureCount, failureCount > 0 {
takeScreenshot()
}
If you are using Xcode 9 already, the takeScreenshot function can use the new API (If not, then use the workaround mentioned by the other answer) :
let screenshot = XCUIScreen.main.screenshot()
let attach = XCTAttachment(screenshot: screenshot)
add(attach)
You can also name the attach and change its lifetime ;)
See Apple's documentation for how to use and where to find them (the "Report navigator" in View > Navigators > Reports) in more detail.

You don't have to integrate Fastlane Snapshot for this. The only trick Snapshot is doing to force screenshot is triggering this code:
XCUIDevice.shared().orientation = .unknown
This will not alter UI as described on Snapshot documentation.
Unfortunately this will not work if you're using expectation for your predicate and you put this code into waitForExpectations(timeout:handler:) handler closure and I don't know why.
To workaround this you can create your own XCTestObservation handler like this:
class MockObserver: NSObject, XCTestObservation {
func testCase(_ testCase: XCTestCase, didFailWithDescription description: String, inFile filePath: String?, atLine lineNumber: UInt) {
XCUIDevice.shared().orientation = .unknown
}
}
XCTestObservationCenter.shared().addTestObserver(MockObserver())
You can put this code in either setUp() method or specific test... method.
The test output is a little weird as it will show "Set device orientation to Unknown" as an error and actual predicate error inside but you will have your screenshot:

You can override the recordFailure method to capture screenshots on any kinds of failures.
override func recordFailure(withDescription description: String, inFile filePath: String, atLine lineNumber: Int, expected: Bool) {
add(XCTAttachment(screenshot: XCUIScreen.main.screenshot()))
super.recordFailure(withDescription: description, inFile: filePath, atLine: lineNumber, expected: expected)
}

Related

Why does Sonarqube mark try as a critical issue?

I'm currently facing an issue with some SonarQube's analysis being performed over some Kotlin code I wrote.
I'm trying to implement a method that connects to the database and returns accordingly to the query's result. I'm not sure how related this can be, but I added the following maven dependencies to my project:
Quarkus
Arrow
Ktorm
The code is the following:
#ApplicationScoped
class Repository(private val database: Database) {
override fun get(name: String): Either<Error, Brand> =
try {
database.brands.find { it.name eq name }.rightIfNotNull {
MissingBrandError("Missing brand")
}
} catch (e: Exception) {
Either.Left(DatabaseError(e.message))
}
}
class Error(val message: String)
class MissingUserError(val message: String) : Error(message)
class DatabaseError(val message: String? = null) : Error(message ?: "Some database error")
NOTE: Database object is of type org.ktorm.database.Database and brands is of type org.ktorm.entity.EntitySequence
The code is working and I also wrote unit tests for it that pass and give enough coverage (accordingly to the code coverage analysis tool), but at some point in my pipeline SonarQube marks the try as a critical issue with the following message:
Possible null pointer dereference in (...)Repository(String) due to return value of called method
I checked it online and I could find some related questions, but none of the provided answers worked for me. Amongst the many attempts these are the ones I can remember I tried without any success:
Not inlining any code (pretty much using Java style code)
Extracting the query result to a variable
Check with if/else statements for nullability instead (both with inlined try and without)
I'd also like to highlight that all I can see on Sonar is the generated report and CLI for the running build. I don't have access to any of its configuration or intended to change them (unless of course it comes down to that). The line I mentioned seems to be the only one affected by this problem according to Sonar's report, that's why this is the solo class I provided.
I hope I provided enough info and that any of you can help me with this. Thanks in advance.

UI Testing Failure

Here is auto generated code. I didn't modified it.
func testExample() {
let app = XCUIApplication()
let defaultStaticText = app.tables.staticTexts["Default"]
defaultStaticText.tap()
}
Error I got is:
UI Test Activity:
Assertion Failure: UI Testing Failure - Failure getting list of active applications: AX error -25205
Screenshot of the app:
All I did during the test is tapping on the Default row on top. I did this in Simulator. The error appears on the last line: defaultStaticText.tap().
Have you tried to use completely different names for “Default” and “Default configured”?
I think you get multiple cells for
let defaultStaticText = app.tables.staticTexts["Default"]
You can learn more from here.

Swift 2, warning: could not load any Objective-C class information from the dyld shared cache

I have found a few questions regarding this issue, yet none of them were helping with my problem. I am trying to save an object to core data using this code (which worked perfectly fine in Xcode 6 and Simulator...):
let fetchRequest = NSFetchRequest(entityName: "Patient")
let fetchedResults : [NSManagedObject]!
do {
fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as! [NSManagedObject]
patienten = fetchedResults
} catch {
print("error")
}
I added the do-try-catch once I started working on this project in the Xcode 7 beta and a physical device.
Now, when I hit the Save button, this piece of code is called, the app freezes and I get the following:
warning: could not load any Objective-C class information from the dyld shared cache. This will significantly reduce the quality of type information available.
Does anybody know where I went wrong?
For anyone coming across this in the future, I just ran into this problem myself and it turned out that I was actually getting a stack overflow from a recursive function.
Apparently calling setValue:forKey: on an NSObject calls the respective set[Key] function, where [Key] is the (capitalized) name you gave to the forKey section.
So, if like me, you have code that looks like the following, it will cause an infinite loop and crash.
func setName(name: String) {
self.setValue(name, forKey: "name")
}
Choose Product > Clean
I had similar issue. I deleted the app from the device. Then "Product->Clean" in the XCode menu. When I ran the app again, the issue got resolved.
Swift 3:
Actually this problem happened often when you have any property in input declared as type NSError and the compiler expect an Error output, so change the input type to Error usually solve this issue.
What helped me in similar problem (xCode 7, Swift 2):
reading this question
Or more quickly without explaining the reason of solution: just comment #objc(className) in your NSManagedObjectSubclass , that was generated from your CoreData Entity (#objc(Patient) - in your case ).
This solution (if issue still appears) does not applicable to xCode 7.1/Swift 2.1, as the way of generating NSManagedObjectSubclasses was changed.
Don't forget about cleaning your project (Product > Clean) and deleting the app from your device/simulator to replace CoreData storage on it.
let fetchRequest = NSFetchRequest(entityName: "Patient")
do {
let fetchedResults = try managedObjectContext!.executeFetchRequest(fetchRequest)
print("\(fetchedResults)")
} catch {
print("error")
}
The above code worked for me.
Maybe the issue maybe with how your core data is managed.
Check if your managedObjectContext is actually getting created.
Check the modelling of your core data

Turn off FireFox driver refresh POST warning

I have inherited some GEB tests that are testing logging into a site (and various error cases/validation warnings).
The test runs through some validation failures and then it attempts to re-navigate to the same page (just to refresh the page/dom) and attempts a valid login. Using GEB's to() method, it detects that you are attempting to navigate to the page you are on, it just calls refresh - the problem here is that attempts to refresh the last POST request, and the driver displays the
"To display this page, Firefox must send information that will repeat any action (such as a search or order confirmation) that was performed earlier"
message - as the test is not expecting this popup, it hangs and the tests timeout.
Is there a way to turn off these warnings in Firefox webdriver? or to auto-ignore/accept them via Selenium or GEB?
GEB Version: 0.9.2,
Selenium Version: 2.39.0
(Also tried with minor version above: 0.9.3 & 2.40.0)
Caveats:
I know about the POST/Re-direct/GET pattern - but am not at liberty to change the application code in this case
The warning message only causes an issue intermittently (maybe 1 in 5 times) - I have put this down to speed/race conditions whereby the test completes the next actions before the message appears - I know a possible solution is to update tests to wait for message to appear and then accept, but my question is, is there a global setting that can just avoid these being triggered/displayed?
That refresh() is there to work around an issue with IE driver which ignores calls to driver.get() with the same url as the current one.
Instead of monkey patching Browser class (which might bite you somewhere down the line or might not) I would change the url of your login page class. You might for example add an insignificant query string - I think that simply a ? at the end should suffice. The driver.currentUrl == newUrl condition will evaluate to false and you will not see that popup anymore.
If I understand you issue properly this might help. In Groovy you can modify a class on the fly.
We use Spock with Geb and I placed this in a Super class which all Spock Spec inherit from. Eg: QSpec extends GebSpec.
It is the original method slightly modified with the original code commented out so you know what has been changed. I use this technique in several required places to alter Geb behaviour.
static {
Browser.metaClass.go = { Map params, String url ->
def newUrl = calculateUri(url, params)
// if (driver.currentUrl == newUrl) {
// driver.navigate().refresh()
// } else {
// driver.get(newUrl)
// }
driver.get(newUrl)
if (!page) {
page(Page)
}
}
}

Observing changes in HealthKit data using HKObserverQuery

When I setup an HKObserverQuery, the update handler always gets immediately called (something I didn't expect). It also gets called when I add data points through Health.app, as you would expect. I am tending to think I am not doing something right with the completion handler, but the docs are fairly sparse on what is supposed to happen here.
Question: Below is basically what I'm doing. Is this expected behavior, or am I missing something?
func listenForUpdates() {
let bodyMassType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)
let updateHandler: (HKObserverQuery!, HKObserverQueryCompletionHandler!, NSError!) -> Void = { query, completion, error in
if !error {
println("got an update")
// ... perform a sample query to get the actual data
completion() // is this the right thing to do?
} else {
println("observer query returned error: \(error)")
}
}
let query = HKObserverQuery(sampleType: bodyMassType, predicate: nil, updateHandler: updateHandler)
healthStore?.executeQuery(query)
}
Edit: discovered completion handler should only be called when there wasn't an error, so moved into the !error block. An error is present when the app is not authorized.
Yes, this is expected behavior. The update handler will always be called on first execution so that you can use it to fetch your initial data (from your sample query, anchored object query, etc) and populate your UI.
The completion handler is only necessary if you intend to use background delivery, it informs HealthKit that you have received and processed the data you need so that HealthKit knows to stop launching your app in the background. If you have not registered your app for background delivery, then the completion handler is essentially a no-op and you don't need to worry about it.

Resources