قالب وردپرس درنا توس
Home / Apple / Local API Conversation Tutorial with WireMock and UI Tests in Xcode

Local API Conversation Tutorial with WireMock and UI Tests in Xcode



WireMock is a development tool that provides a local copy of the results of an external API call. You can use it in the context of user interface tests (UI) to provide different types of API responses.

UI testing acts as a safety net, ensuring that you deliver the intended user experience. It focuses on the end-to-end experience of your app. UI testing can also answer questions other than device testing can, for example:

  • What happens on this screen if my vision is empty?
  • What happens if I get an error from a network request?
  • Does my Validation Interface show the correct information for different types of input?
  • Do my table cells reflect the data received?

Getting Started

First Find Download Materials button at the top or bottom of this tutorial. Download the startup project and open it. Build and run the project to check the functionality.

The Star Wars Info app uses the Star Wars API to retrieve a list of Star Wars characters. You can tap each cell to navigate to a detail screen for that character.

 Example app

Run the tests

The app is already set up with a UI test target and two UI tests. The UI test target is already connected to the standard Test of the construction schema.

Run the tests by pressing Command-U or by clicking the menu item Product ▸ Test . You will see the app launch and navigate from list view to detail view and back. During this navigation, the UI tests validate expected conditions on both the list and the detail views.

To view the test report, go to Report Navigator in the left pane and see the test results: [19659002]

As long as your computer is online and the tests can be successful with the Star Wars API, The tests pass.

UI Test Code

See StarWarsInfoUITests.swift to see the UI tests. The details of UI testing are beyond the scope of this article. See the iOS Unit Testing and UI Testing Tutorial for a deep dive on that topic!

The example project demonstrates best practices, including:

  • Use accessibility identifiers, not accessibility labels, to identify items when possible. The advantages of this approach include:
    • Accessibility identifiers are not available to the user in any way, so you can name them whatever you want.
    • The identifiers remain constant. For example, a label may have different values, but the identifier remains constant. So your tests will still be able to find the item on the screen.
  • A convenience method called waitForElementToAppear (_: file: line: elementName: timeout :) in an extension of XCTestCase allows for custom timeouts.
  • Passing the file and line numbers to convenience features allows the UI test system to report test errors at the call point.

Current Setup Challenges

This small app and its related tests work well in the current configuration, but as the code base grows, the app will rely on many more network requests. Tests will slow down and become dizzying.

Here are some issues you can predict as the codebase grows:

  • What happens if the network request you trust fails?
  • What happens if the network slows down, causing a timeout or longer latency?
  • What if the list request starts sending data back in a different sort order?

In all three scenarios, your tests would fail. It is a common denominator. None of these test errors are caused by a programming error or a logical problem. Test errors in these scenarios would be false positives, because the error would be beyond your control.

Limiting network failure

In this tutorial you will eliminate one of the sources of uncertainty in UI testing. You're going to stop making direct network requests and rely on a network-wiring technology called WireMock. WireMock allows you to eliminate the network as a potential source of error in UI testing.

Using WireMock, you always get the same answer for a given query on each test run. This allows you to have more confidence in your tests. If there is a mistake, you will spend less time trying to figure out why. The network is one of the more volatile components of all applications. By removing it from the list of possible causes of errors, you will be able to focus on the base functionality you are trying to test with each screen.

WireMock overview

WireMock is an open source library that allows you to run a local web server for testing. By updating the API endpoints you call from your app while running a test, you can return familiar answers. This can help you move faster during development. By trying your app, you can receive expected responses from API requests by fixing your app on a WireMock instance.

Enough of the theory. Time to get started!

Setting up WireMock

WireMock requires at least Java 7, so make sure your Mac has an updated version of Java installed. Download the latest Java Development Kit, JDK from Oracle. You run WireMock as a standalone entity. Go to the download page and get the latest stable design of WireMock. The download will be a single JAR file.

Go to the startup directory of the startup project and create a directory named Vendor . Move the JAR file to this directory. Rename the JAR file WireMock.jar to make it easier to interact with from the command line. Then open the Terminal and navigate to the Vendor directory you created.

Inside this directory, type the following command to start the WireMock process:

  java -jar wiremock.jar --port 9999 --verbose

This command starts WireMock on port 9999 and turns on verbose mode. When you turn on verbose mode, you can see all messages, including URL hits and near-misses when your app interacts with the WireMock endpoints. This is a great help when trying to figure out why a WireMock endpoint doesn't work.

Now test that WireMock is running on your system. Open a web browser and go to http: // localhost: 9999 / __ admin / mappings.

You should see the following:

  {
"mappings": [ ],
"meta": {
"total": 0
}
}

Great! This means that WireMock is up and running. You will also notice that WireMock has created two directories in the provider: __ files and mappings .

 Files and Maping Directories

By default, the standalone occurrence of WireMock looks for these two directories relative to the executable location.

  • __ files directory contains the mock replies you want to send back for all given API requests.
  • mappings directory contains files that map API request patterns to specific files containing the desired response.

Setting up a mock response

The sample app uses one end point from the Star Wars API to request a list of characters. That endpoint is https://swapi.co/api/people/.ebrit19659002??If you go to this endpoint in a browser, the Star Wars API page does a great job of showing you the JSON response, including headers. You are only interested in the answer here.

 People endpoint

Copy the full answer except the headers:

  {
"count": 87,
"next": "https://swapi.co/api/people/?page=2",
"previous": zero,
"results": [
    {
      "name": "Luke Skywalker",
      "height": "172",
      "mass": "77",
      "hair_color": "blond",
      "skin_color": "fair",
      "eye_color": "blue",
      "birth_year": "19BBY",
      "gender": "male",
      // Clipped for brevity here
  ]
}

Save this answer in the __ files directory as character-list.json .

Setting Up Mappings

Then you must let WireMock know how to map the map people endpoint for this answer. Create a new file in the mapping directory named character-list.json .

Make the contents of the file look like this:

  {
"request": {
"method": "GET",
"url": "/swapi.co/api/people"
}
"response": {
"status": 200,
"bodyFileName": "character-list.json"
}
}

This lets Wiremock know about mapping any GET request with the /swapi.co/api/people path to the character-list.json response with an HTTP 200 success status.

Your final provider directory layout should look like this:

 Supplier directory setup

Verify functionality

Now, confirm that WireMock has your mapping indexed. If your terminal is still running from the previous session, stop it by pressing Control-C and restart WireMock using the command:

  java -jar wiremock.jar --port 9999 --verbose

WireMock should have retrieved the new mapping file. To check this go to http: // localhost: 9999 / __ admin / mappings.

You should see the following output:

  {
"mappings": [ {
    "id" : "5804b634-2a15-4fb0-a16e-2c19559f37df",
    "request" : {
      "url" : "/swapi.co/api/people",
      "method" : "GET"
    },
    "response" : {
      "status" : 200,
      "bodyFileName" : "character-list.json"
    },
    "uuid" : "5804b634-2a15-4fb0-a16e-2c19559f37df"
  } ],
"meta": {
"total": 1
}
}

This output means that WireMock retrieves your mapping files. Next, check the response returned for your endpoint. Go to a web browser and type http: // localhost: 9999 / swapi.co / api / people.

You should see the following output:

 LocalHost Output

Excellent! Everything works from the WireMock page. You now integrate WireMock into your UI tests.

WireMock Integration

To integrate the WireMock instance into your UI tests, follow these steps:

  1. Set up your project to allow HTTP loads from a locally running WireMock instance.
  2. Start your UI tests with launch arguments to pass a defined argument.
  3. Create a way to swap URL schemes in code based on the launch argument.

Allow load from a local HTTP server

First, configure your project to allow HTTP requests from a non-SSL connection. If you do not go through this step, App Transport Security (ATS) does not allow network requests to connect to WireMock.

Allowing insecure loads is normally not advisable, but for UI testing it is easier than configuring a self-signed certificate to enable WireMock SSL. In a more complex setup, you would configure the project to allow only insecure loads from your test schema with a configuration setting or other Info.plist for your test target.

Follow these steps to set up ATS for UI testing with WireMock:

  1. Open Info.plist in StarWarsInfo group.
  2. Click the bottom + on the last row.
  3. Select App Transport Security Settings from the drop-down list in the Key column .
  4. Press Tab .
  5. Select the detection triangle to the right so that it points downwards.
  6. Well, click + in App Transport Security Settings row.
  7. From the drop-down list in the new row, select Allow Any Load .
  8. Finally, in the Value column for this row, change the value to YE S .

  Security Settings for App Transport

Defining a Launch Argument

Then you define a launch argument to let the app know when to change URL forms. When you start from Xcode, you can send arguments to your app just as you can send arguments to a terminal command. Use Scheme Editor in Xcode for this.

You will define the launch argument during the scheme. By configuring things this way, you can run the app in the simulator without any tests running. Once you're sure that everything is running the way you want, define the same arguments in the test code and launch the app with them. When running your UI tests, the app should behave the same way.

Editing the Xcode scheme

Click the StarWarsInfo form in Xcode and click the Edit Scheme .

 Edit Scheme

Select the Run action in the left pane. In the middle pane, select the Arguments category. In the Arguments Passed on Launch section, click + and add the -runlocal launch argument. Be sure to check the box to the left of the new launch argument.

 Adding a launch argument

Click Close to accept the change and return to your code. [19659102] Update to use launch arguments

Now you need to update the code base to look for this launch argument and use the correct URL form.

Adding a tool function

First, create a usage method to look for the launch argument. Control-click on the StarWarsInfo group, and create a new group named Utils . Then Control-click on the new Utils group and create a new Swift file named StartupUtils.swift . Add the following code to this file:

  struct StartupUtils {
static func shouldRunLocal () -> Bool {
return CommandLine.arguments.contains ("- runlocal")
}
}

This code inspects CommandLine.arguments which are a number of strings. If that array contains the -runlocal argument, the function returns true .

Check for local flag in Network Client

. Then use your new convenience feature in the network client. Inside the Network open StarWarsAPINetworkClient.swift . Update the property for baseURLString to a calculated property that looks like this:

  was baseURLString: String {
if StartupUtils.shouldRunLocal () {
return "http: // localhost: 9999 / swapi.co / api /"
} else {
return "https://swapi.co/api/"
}
}

If the property -runlocal is present at launch, baseURLString will use the local scheme and connect to the running WireMock instance for its data. You now have full control over the data response from the API.

Open Terminal if it is not already open and ensure WireMock is running. Navigate to the directory containing the WireMock executable. Run the same command to start WireMock:

  java -jar wiremock.jar --port 9999 --verbose

Perform a race with WireMock

With your launch arguments set, build and run by pressing Command-R . Look at the terminal while the original list is loading. You should see the console printout when the app requests network data and WireMock returns it.

Testing against WireMock

Now complete the final steps to get your UI tests set up with the right launch arguments.

UI Test Launch Arguments

Back in Xcode, expand the StarWarsInfoUITests group in the project navigator. Then Control-click on the Utility group. Create a new file named LaunchArguments.swift . Be sure to check ** StarWarsInfoUITests ** and remove the check from ** StarWarsInfo ** in the ** Target ** section of the ** Save as ** dialog.

 Target Membership [19659002] Add the following code:

  struct LaunchArguments {
static was launchLocalArguments: [String] {
return [
      "-runlocal"
    ]
}
}

Finally expands the group Tests in the project navigator and open StarWarsInfoUITests.swift . In each of the two tests, right after creating the local app add the following line:

  app.launchArguments = LaunchArguments.launchLocalArguments

This adds your runlocal launch argument to each test.

Now you can test the UI test running against WireMock instead of the live network.

Switch back to Xcode and run your UI tests by pressing Command-U . If you look at Terminal while the tests are running, you will see output from WireMock when your tests request data and they are returned from the local server.

 Tests Running

Congratulations! You have eliminated the network as a dependency on your UI tests.

Where to Go From Here?

In addition to spotting the same answers you always get from live network requests, you can now return different scenarios to the UI and see how the app responds.

For example:

  • Does your app display correct error messages if it receives an HTTP 500 status?
  • What happens to your UI if it receives a blank dataset?
  • Does the app respond to the right if a malformed data set is received?
  • What happens if any of the strings received are too long for the UI? Is everything packaged or shortened as expected?

Try adjusting your scoffed responses to test different streams in your apps. WireMock also allows you to vary responses by many different variables for the same endpoint, such as query parameters and headers. Check out the documentation to see what else is possible.

Good luck and leave a comment for the comments!


Source link