Mocking URL request with Xcode 7 UI Testing

Mocking URL request with Xcode 7 UI Testing

I started answering a question on Apple’s new dev forums and decided it would be better as a simple blog post.

(The question was “UI Testing Mocking Data Xcode 7”, https://forums.developer.apple.com/message/25973 )

Super simple overview of problem I want to solve:

  • New UI Tests run in separate process from app, so my old way of mocking URL responses would no longer work
  • I did not want any dependencies on external servers or processes
  • I wanted to have everything about my UI Test case embedded in the test case file itself
  • It needs to run on a CI server like Jenkins

My solution (currently a work in progress)

My solution is a little more involved than I would like, but I think it will work pretty well for me.

To make this work, I need to do some minimal setup in the app itself to make it “test ready”. Since there are only two ways a UI Test case can cause changes of any kind in the app:

  1. The actual UI interaction (the main point of the test)
  2. The launch arguments for XCUIApplication
#2 seemed the reasonable choice.

Summary of changes to the app itself

My goal was to make the minimum set of changes to the main app as I could:

  • Added a class, TestingProxyClient, which uses NSURLProtocol to call a proxy for all network requests
  • Added some helper functions in StartupControl.m to interpret the launch arguments passed from the test case (StartupControl also initializes the proxy client)
  • Updated app’s main.m to call the setup function defined in StartupControl

Summary of changes to the test cases

My intent is for the test case to embed everything needed in the same place.
 
The following are mostly finished:
  • Start a embedded webserver to act as a network proxy (using BCDWebServer, available on github)
  • Use launch arguments of XCUIApplication instance to pass values to the app (including the proxy address & port)

Still to do:

  • Port my existing mock data supplier to this new structure

Other changes

Update #3 (01-Aug-2015): Back to red: The dialog started appearing again for reasons I cannot discern…bummer.  (I made no changes to version of Xcode, beta or production, so I’m confused)


Update #2: Apparently the answer is much simpler and I was over thinking it. I’ll hold off final judgment for a day or two though.  I added ‘Xcode-beta.app’ to the exception list in the firewall options panel and it seems happy again.



Update: This apparently does not fix the issue — the dialog started appearing again today and what I thought fixed it yesterday does not work today…hmm, back to the drawing board.

One other change was necessary to ensure I could run this on a CI server without any user intervention. When you start up a web server in the simulator, I was getting the application firewall permissions prompt every time.  Obviously, turning off the firewall completely would remove the prompt, but seems like a bad idea.

Screen Shot 2015 07 20 at 9 34 43 AM

It probably took me longer than it should have to figure out the answer, but it is reasonably straight forward. There are two steps:

  1. ensure the application firewall’s automatically allow signed apps permission is checked
  2. ensure the XCTRunner binary is signed (this is the actual OS X app that runs the UI tests, not sure why it wasn’t signed by default on my machine)
#1 is simple:
  • select Security & Privacy from settings
  • click the Lock icon to make changes
  • click the Firewall options button
  • ensure the “Automatically Allow Signed Apps…” checkbox is checked
I found the answer to #2 from this blog post, just open up a terminal window and execute these steps:
  • /usr/libexec/ApplicationFirewall/socketfilterfw –setglobalstate on
  • /usr/libexec/ApplicationFirewall/socketfilterfw -s “/Applications/Xcode-beta.app//Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/XCTRunner.app/XCTRunner”
 

Tentative conclusion

I think I have solved all the issues that would prevent this from working and am finishing up the implementation now

This approach lets me put almost everything in just the test case: UI steps, data to mock, etc. which should make it reasonably straightforward to run on our CI server (Jenkins).  

This will also let individual test cases setup the mock data supplier with the URLs and responses to mock.

Hopefully this will help someone else out there