Detect HTTP response using automation instruments in Xcode - ios

I am using the automation instruments in Xcode. I would like to check the response I get when I use a HTTP connection. I don't know if this is possible.
Here is my code:
- (void)readMe
{
[HTTPConnection sendGetToUrl:[NSURL URLWithString:READ_ME_URL]
target:self
selector:#selector(readMeFinished:responseCode:)
failSelector:#selector(downloadFailed:)
userInfo:nil];
}
- (void)readMeFinished:(NSData *)recievedData
responseCode:(NSString *)responseCode
{
//If the response is a 200 OK.
if ([responseCode isEqualToString:#"200"]) {
// HERE, I WOULD LIKE TO DETECT IN AUTOMATION
// THAT I'VE RECEIVED A 200 AND PASS THE TEST
}
}
- (void)downloadFailed:(HTTPConnection *)connection {
// HERE, I WOULD LIKE TO DETECT IN AUTOMATION THAT
// THE DOWNLOAD HAS FAILED AND DON'T PASS THE TEST
}

The automation instrument has no way to reach into your Objective-C code and check for results. It's only access to your application's state is through the user interface.
So, to detect if you have a HTTP 200 status code, you could expose that fact somehow with an invisible label. That would be a brute force way to expose inner application state to the automation instrument.
However!...
I would recommend instead testing for what the user sees if the network connection completes successfully. That's the best way to think about how to use the automation instrument. It's an integration testing tool focused on testing what the user sees and experiences by looking for the state of controls and text on the screen.
You don't provide enough context about what your code actually does in the app, but assuming that the method -readMe shows something to the user that they have to read and approve by clicking a button, I would check to for the result after telling the automation to click the button like a user. What does the user see if there isn't a 200 status code? Do they see an error message? Then to assert that the network code succeeded, check to make sure there isn't an alert window with that error message.

For test automation purposes I think your solution would be better off as a Unit test versus using UI Automation. UI Automation is better used when you have a test case that involves navigating around your application and checking the flow and that expected UI elements are appearing and displaying properly.
If you prefer to stick to UIAutomation, I would recommend maybe creating a function that checks the fields .value() to be not equal to whatever they were initiated as, you can throw this into a while loop with a UIATarget.delay(x) that way you're only checking ever X seconds. I suggest this because then you can add the conditional of if the fields aren't filled then you can throw an error message in your automation test.

Related

Promise resolve not called because popup closes too fast

I have the following piece of code:
browser.windows.create({ url: urls }).then((newWindow) => {
newWindow.tabs.slice(0, pins.length).map((tab, index) => {
browser.tabs.update(tab.id, { pinned: true })
})
})
It works great when I prevent the popup from closing (via webdev tools) but in a regular use case, the resolve is not triggered.
I struggled with this same problem when I started working on my addon (In My Pocket for Firefox, you can have a look at the code on bitbucket if it can help, I implement exactly what I'm about to explain). I needed to trigger network calls to an API from the popup and do something when the request was succesful, and I got the exact same problem.
What you have to keep in mind is that code from a popup is executed as long as the popup is open. Closing the popup is like closing a webpage that contains Javascript: all its JS code is "unloaded", code execution stops, there's nothing more to handle the resolved promise.
A way to circumvent this is to implement the actual behaviour in a background script that will always be running all the time, and communicate between the background script and the popup via messages, through the runtime.sendMessage method and an event listener setup with runtime.onMessage.addListener.
Background scripts are the place to put code that needs to maintain long-term state, or perform long-term operations, independently of the lifetime of any particular web pages or browser windows.
You can get fancy with messages (sharing objects and not plain string) but you get the idea. And this way, your code that does not resolve immediately will still get processed all the way until it's finished, even if the popup is closed before the processing is over.

Revulytics data not showing in Dashboard

I am using Revulytics SDK to track feature usage and came across the below problem.
I am sending feature usage after properly setting up the SDK configuration etc, using the EventTrack() method like this:
GenericReturn grTest = telemetryObj.EventTrack("FeatureUsage", textBoxName.Text.ToString(), null, false);
This returns OK and usually, I can see the usage data in the dashboard. However, after multiple tests, the data I am sending does not show up on the dashboard.
Can anyone hint me how to debug this? Thanks for any help!
I hit a similar issue when first working with this SDK.
I was able to address this as soon as I understood the following:
There are event quotas for the incoming events;
Event names are used for making the distinction.
So when I was sending dummy test data, it made it there, but when I sent some demo data for stakeholders, it was not showing up.
I think the same happens here. You're getting the event name form textbox.text... Pretty sure that varies every time you run the code.
Here are the things to keep in mind when testing your code:
the server has a mechanism to discard / consider events;
implicitly, it allows first xx events depending on the quota;
if you are sending more than xx events, they will not show up in reports.
So, you must control which events to discard and which to consider (there are a couple of levels you can configure, and based of them you can get the events in various types of reports).
Find the "Tracked Events Whitelist Management". You will be able to control these things form there.
This blog helped me (it is not SDK documentation): https://www.revulytics.com/blog/getting-started-with-usage-intelligence-part2-event-tracking
Good luck!

Using Xcode UI tests to test underlying framework behavior

I am building an iOS framework that collects information about various events in an iOS app and performs local and remote analysis. Some of these events can't be tested outside of an app: for example, view controller transitions. To test these events, I built a test iOS app and would like to use Xcode UI tests to:
Initiate a view controller transition by, say, tapping a button that pushes a VC to a navigation controller, or by switching to a different tab in a tab controller
Verify that the framework was able to detect the view controller transitions and generate the necessary events (e.g., send them to the server)
The problem is that Xcode UI tests run outside the app, so I can't access any shared objects to verify that the framework is functioning properly. I also can't insert any mock objects, because, again, I don't have access to the framework loaded in the app's process.
I went as far as trying to load the framework in a kind of test mode and have it update a label with some results that the UI test would then be able to read. But even that is difficult, because XCUIElement doesn't give me the actual text of the element; I can only query for elements with some predefined text. I can sort of work with that, but it seems like a very roundabout approach for something that should be rather trivial.
Are there any better alternatives for testing events that can only be simulated in a running app, and then verifying that my framework was able to detect and process them? In addition to view controller transitions, I am also interested in touch interactions, accelerometer events, and other features that can't be simulated outside of an app. Naturally, I can simulate these events for unit testing, but I would also like to automatically test that when these events occur in an actual app, the proper responses are generated in my framework.
You could try using SBTUITestTunnel. This library extends the functionality of UI Tests adding some features that might come in handy in cases like yours.
Network monitoring
The library allows your test target to collect network calls invoked in the app. Usage is pretty straight forward:
func testThatNetworkAfterEvent() {
// Use SBTUITunneledApplication instead of XCUIApplication
let app = SBTUITunneledApplication()
app.launchTunnelWithOptions([SBTUITunneledApplicationLaunchOptionResetFilesystem]) {
// do additional setup before the app launches
// i.e. prepare stub request, start monitoring requests
}
app.monitorRequestsWithRegex("(.*)myserver(.*)") // monitor all requests containing myserver
// 1. Interact with UI tapping elements that generate your events
// 2. Wait for events to be sent. This could be determined from the UI (a UIActivitiIndicator somewhere in your app?) or ultimately if you have no other option with an NSThread.sleepfortimeinterval
// 3. Once ready flush calls and get the list of requests
let requests: [SBTMonitoredNetworkRequest] = app.monitoredRequestsFlushAll()
for request in requests {
let requestBody = request.request!.HTTPBody // HTTP Body in POST request?
let responseJSON = request.responseJSON
let requestTime = request.requestTime // How long did the request take?
}
}
The nice thing of the network monitoring is that all the testing code and logic is contained in your test target.
Custom block of code
There are other use cases where you need to perform custom code to be conveniently invoked in the test target, you can do that as well.
Register a block of code in the app target
SBTUITestTunnelServer.registerCustomCommandNamed("myCustomCommandKey") {
injectedObject in
// this block will be invoked from app.performCustomCommandNamed()
return "any object you like"
}
and invoke it from the test target
func testThatNetworkAfterEvent() {
let app = ....
// at the right time
let objFromBlock = app.performCustomCommandNamed("myCustomCommand", object: someObjectToInject)
}
Currently you can only inject data from the test target -> app target. If you need the other way around, you could store that data in the NSUserDefaults and fetch it using the SBTUIApplication's userDefaultsObjectForKey() method.
I personally don't like the idea of mixing standard and test code in your app's target, so I'd advise to use this only when really needed.
EDIT
I've update the library and starting from vision 0.9.23 you can now pass back any object from the block to the test target. No need for workarounds anymore!
Not much of an automated way to have things done, but do use breakpoints as an intermediate way, at least until you get things started automatically. You can add a symbolic breakpoint to hit on UI methods such as [UIWindow setRootViewController:], viewWillAppear, etc.
You can explicitly define modules, conditions and actions, so that whenever someone views your VC, some action will be done that might be helpful to you.
If you can print something from your framework using a simple "po myEvent" upon some viewDidAppear, you're good to go. I've never tried using fancier methods as an action, but those can be done as well.

Best practices for showing an error

A part of my app makes a POST request to a web service. I have it working but now I am in the "lets see how we can break it" phase. I have discovered that if the user does not have a network connection (cell/wifi) then the POST didFailWithError and essentially just sits there with no indication of passing or failing. I know how I can handle the error (as in where to put the code) but I am wondering what would be the best thing to do? Should I set an alert such as "Please check your internet connection". Also should I make it a full popup or should I just have a label that I set text to?
This is my first time making an App that will actually be used so any other advice as far as alerting the user and best practices with that I'd love to hear.
This is mostly up to you, as either method would be fine. If the action is something the user actively interacts with (e.g., presses a button prior to it occurring), an alert would probably be best. Otherwise, if the action happens in the background or with little user interaction, I would recommend using a label or some other visual cue. Often, in apps that have a feed you'll see a UIView appear above/below the feed saying "network unavailable" or similar.

background file uploader?

So after two days of googling incessantly and apparently asking the wrong questions, I think I have figured out a way to word it so I get the response I'm looking for.
I have a Project Management application, written in MVC3. Sometimes, the users have to attach large files and upload them to the applications. (100-200 mb) is typical. The problem of course is that this is currently handled synchronously, and varying network speeds mean that the application can be completely blocked for 10 minutes to an hour if someone's on a slow connection. FTP is NOT an option here (my hands are tied by our network guys on that one).
So I am looking for a way to do the following workflow:
user clicks Upload File
user selects File to upload
user clicks "Go" or whatever button
Application says "your file is being uploaded. You will be notified when it's complete"
user continues to use the application as normal.
Some things to be aware of: I already have an internal messaging system implemented. So when I say that the app will notify the user when it's complete - all it needs to do is insert a new message into the queue. It DOES NOT need to notify the user's current screen or anything like that - so I'm not worried about a return value of any kind. I also have a background Error log implemented, so I can insert a message into the log if something goes wrong and again - inform the user via the internal messaging system.
So I am stumped on how to implement this. I thought an Async Controller was the right way to go, but if I understand all the stuff I've been seeing - it's not. Feel free to correct me. I implemented a version using Async but when addressing the one problem it had, I was informed that I was doing it wrong anyway.
So uh...help? I'm all ears.
If you can use 3rd party controls then take a look at the Telerik controls:
http://www.telerik.com/products/aspnet-mvc/upload.aspx
It has an Asynchronous File Upload control.

Resources