UIAutomation bringing iOS app to a consistent state - ios

I writing JavaScript test for my iOS app. I am hoping of using Apples Profiler and UIAutomation.
1) I was wondering how can I reset the app every time I run the test. I would like to reset my app to a consistent state every time before I run a new test. Have separated my tests into few groups. Every test of the first group should start on the first screen containing a tableView and filters for sorting elements in that table should be set to a consistent state. Second group of tests should start on the Settings screen and some options/switches should be pressed in particular order for me to test the UI.
2) Also first time the app starts there is a tutorial. How can I make the app think it is freshly installed and test the tutorial feature.
Thanks for the answers

How can I make the app think it is freshly installed ?
As the iOS applications are sandboxed, the only way is to delete and re-install the app every time.

In the Illuminator framework that I wrote (which extends UIAutomation), we provide an automation bridge that allows us to send "reset" commands to the app, putting it into a known state before each test is run. This makes the testing very repeatable, even if some tests fail.
Additionally, the command line scripts can recover the test run even if the app crashes.

Related

"Cached element do not exists in DOM" when run Appium with Robot Framework

I'm working on automated test using Appium with Robot framework on Android device. I create schedule run on Jenkins. My test flow is entering some data in page A and submit, then switch to page B to check the result and switch to page A to enter a new data. I repeat this loop for around 10+ time. Everything works fine in around 4-5 rounds but after that there show up an error :
StaleElementReferenceException: Message: Cached element 'By.xpath:
//android.widget.TextView[#text='Limit']' do not exists in DOM anymore
The TextView is in the page A. I monitored the robot and saw that the TextView was shown up but the robot did not see it. I tried restart the device but the problem is not solved. I search through the internet and found some who facing the same issue but they use different programming language like Java or Python. I have no idea what I have to do next.
Development Tools :
Appium version: 1.21.0
Robot Framework version: 4.1.2 (Python 3.10.0 on win32)
First I do not use Robot Framework, but the code should be similar according to this https://robocorp.com/docs/languages-and-frameworks/robot-framework/try-except-finally-exception-catching-and-handling.
Second, I'm not sure if this is the best way to get around this. I think there is something you can do with the expected conditions class to get around this in a "cleaner way" but I'm not quite familiar with it enough to show/tell you. Instead what I've done is something like this...
from selenium.common.exceptions import StaleElementReferenceException
while some_limiting_factor:
try:
# logic for submitting page A, assertions for page B
except StaleElementReferenceException:
element = driver.find_element('By.xpath: //android.widget.TextView[#text='Limit']' )
As much as I want to cache elements in appium, it seems that the service itself does NOT want you to, at least not in my experience. Getting a fresh element(s) every time seems to ensure a "slow but steady" test. Hopefully someone can show me the deep appium secrets one day.

Understanding XCTestCase life cycle - How to perform setup BEFORE Xcode unit test launches the app?

I am new to unit tests in Xcode and Swift and have some trouble to understand the life cycle of XCTestCase.
How/where to add setup code which is executed before the actual app is launched?
Problem is, that first the host app is launched before any of the test setup methods (class func setUp(), func setUp(), func setUpWithError()) are executed.
It is even possible to run test code before the host app launches?
Details:
As described in a previous question my app uses a SQLite database to persist some data. When the app launches a database connection is created and data is read from the database.
To make tests consistent and repeatable I would like to use a fresh database with some well defined data every time a run the test. To archive this I tried to override setUpWithError remove the existing db file and move a file with pre-defined data in place instead.
Unfortunately this does not work, because setUpWithError is executed only after the host app was launched. The same is true for all other test setup methods.
Moving a fresh database file in place before running the tests is only an example. The problem is the same for all local data which should be in place before the host app launches to ensure repeatable tests.
An answer to my previous question included a UIApplication extension with a isTesting method which can be used to check if a test is performed. While I could use this in my app code to setup the test data, I would consider this a bad solution. I would like to keep the code completely separated from the production code. Is this possible?
There are several approaches to set up data before running a test case
NSPrincipalClass
As described here you can create a class, and the init method of that class is executed before running any test. This helps setting up dependencies used by many test cases. I don't think this is the way to go in your case.
isTesting
Instead of setting up the code in your app target, you can also check for isTesting early in the didFinishLaunchingWithOptions method of your AppDelegate and simply return there. In this case the regular code is not executed and you can run you custom database setup code in the setup of your test class.
Dependency Injection
Right now you are doing integration testing in my opinion. If you want to have proper unit tests, they should not operate against the database of the underlying app. Instead create an extra (in memory) database and inject that into the code you are testing. I think this is the way you should follow. The benefits are
Your app is not affected by your tests. Next time you start the app to manually test, your original data is still there.
Your unit tests are not affected by manual changes to the app
Your unit tests are also independent of each other. Order of execution doesn't change the results when setting up the database fresh for each test case.
It is even possible to run test code before the host app launches
If you have to ask that, your tests are not unit tests. Unit tests test code, not the app. A test that requires the app to be running would be a UI test.
In fact, the best approach for unit tests is to put all your testable code into a framework and give the framework unit tests; that way, your tests run much faster because the app never has to launch at all.
It sounds to me like the problem lies deeper in your app's code: you have evidently not written your code in such a way as to be testable. So that would be your first move. Writing testable code, and writing unit tests, is an art; you have to separate out the "system under test", which should be your code alone, and make sure you are not testing anything that doesn't belong to you and whose workings are already known — like Core Data.

Difference in manual execution of running app in background and runAppInBackground() using appium

While executing runAppInBackground() for Android application through Appium the app gets restarted but when executed the same manually couldn't be able to reproduce the same. I Would like to deep dive into implementation of a runAppInBackground() method to reproduce the same issue in a manual way.
You need to look behind the code of runAppInBackground
From java client side (your test code) perspective, it is a single call to Appium server:
POST "/session/:sessionId/appium/app/background"
If you continue looking into where its implemented on server side, you finish with appium-android-driver function.
In short what it does:
Get current activity and package
Press physical Home button
Wait for time you provided as argument (seconds)
Bring up back in focus based on different conditions; from the code you can understand what activity is being started
Basically its a sequence of adb shell commands, that you can run from terminal.
My guess is that step 4 you did manually may differ from what Appium is doing: different activities/arguments for activity been called

iOS UnitTests with preloaded sqlite3 DB

I am required to create iOS unit tests, where i need to push an sql .db into app location/sandbox/whatever (may be from testsuit's setUp() func) and run some CRUD operations from the testcases and later delete the db from tearDown() when tests done. How can i push a .db into app location for test, which will be used just like a db used inside app.
Another question, I need to run automated tests, so is there any command (like in Android we use adb, in Tize we use gdb) for iOS app to insert these database file in app location to run those testcases. If i am missing any point please help.
What is the standard way to test CRUD operations in a simulated db for iOS.
As far as my study, there is no adb/sdb like tool for iOS. Moreover there is an workaround for performing test on real database.
Steps
Keep the test-database into App/resource folder. You can skip this large test-database from your release build by following these technique.
Make a function in App side to replaceWithTestDatabase(), which basically replace your App database with App/resource/test-database. You can temporary copy current App db if you need.
If you want to access from AppTests (unit test), you can simply call Aapp/replaceWithTestDatabase as iOS Tests are hosted under main App.
If you want to access from AppUITests, you need to send extra launch argument from setUp() method to let App know that it should call replaceWithTestDatabase from app delegates. Here is a nice explanation how we can send arguments to main App from AppUITests.
Hope it helps.

monodroid activity - wrong activity at start up

I have developed a monodroid application with several activities.
Occasionally, the wrong activity is shown first, as opposed to the one with "MainLauncher=true" set.
Also, occasionally I get a dialog stating "Could not determine activity to run because the generated manifest could not be found?
I occasionally see the wrong activity on startup.
I think this generally seems to be when I haven't changed any code and try to restart my application for a second debugging session.
I think what happens in this case, it that Android tries to restart my application on the page/Activity where I last left it. This situation also happens when the app is in general use - so it's good practice to write your code so that the app works in this situation (e.g. using the saved instance state bundle)

Resources