Appium Testing ReactNative for iOS Simulator in CircleCI, Jan 2021

Sandeep Dinesh
4 min readJan 22, 2021

--

Happy New Year, 2021!

The very first time I tried to take my Appium tests to the Cloud, not the Saucelab/ Applitools way, rather a complete end to end me handling everything my way — from creating my emulator, building my app, deploying it and running my appium based tests, I tried at CircleCI. Back in 2019, and trying to get it all done through Android Emulator, things didn’t turn out the way I wanted and I left it with that.

Come 2020, a new year and a new company I got it done using Github Actions both for iOS and Android. Then I thought where are we at CircleCI in 2021? surely they should be in the game. Guess what? They are in. Definitely for iOS.

My presumption is that you folks know to hook up your git repo to CircleCI; if not its really simple just provide access to your repos and punch in your credentials. I am sure there are blogs for those kinds of things, but this being test automation blog lets spend more time on that.

TLDR; let's see the .circleci/config.yml

# Define the jobs we want to run for this projectjobs:build:macos:  # indicate that we are using the macOS executorxcode: 11.7.0environment:IOS_VERSION: 12.4DEVICE_NAME: TestiPhoneXsteps:- checkout- run: npm install -g react-native-cli- run: |gem install xcode-install- run: |xcrun simctl create $DEVICE_NAME com.apple.CoreSimulator.SimDeviceType.iPhone-X com.apple.CoreSimulator.SimRuntime.iOS-12-4 > deviceid.txtDEVICEUUID=`cat deviceid.txt`echo $DEVICEUUIDxcrun simctl boot $DEVICEUUID &sed -i -e "s/{IOS-DEVICE-UDID}/$DEVICEUUID/g" appium.ios.conf.jssed -i -e "s/{IOS-DEVICE-PLATFORM-VERSION}/$IOS_VERSION/g" appium.ios.conf.jssed -i -e "s/{IOS-DEVICE-NAME}/$DEVICE_NAME/g" appium.ios.conf.jscat appium.ios.conf.js- run: |rm -rf node_modulesnpm cache clean --forcerm -rf package-lock.json- run: gem install cocoapods- run: npm install --save-dev- run: npm audit fix- run: npm install --save-dev chai- run: |cd ios && pod install --repo-update && cd ..- run: |npm start --reset-cache &> metro.log &sleep 30- run: |react-native run-ios --simulator=$DEVICE_NAME &> build.logsleep 30BUILT_APP=`find ~/Library/Developer/Xcode/DerivedData -name HelloWorld.app | grep Build/Products/Debug-iphonesimulator/HelloWorld.app`mkdir -p ios/build/HelloWorld/Build/Products/Debug-iphonesimulatorcp -R $BUILT_APP ios/build/HelloWorld/Build/Products/Debug-iphonesimulatornpm run e2e-test:ios -- Sanity &> test-execution-console.log- store_artifacts:path: metro.logdestination: metro.log- store_artifacts:path: build.logdestination: build.log- store_artifacts:path: test-execution-console.logdestination: test-execution-console.log- store_artifacts:path: __tests__/e2e/test-resultsdestination: test-results# Orchestrate our job run sequenceworkflows:build_and_test:jobs:- build

Explanations for Key Points

  1. Chose the macos executor, why? because it comes with inbuilt hardware for Simulators, XCode, etc.
  2. Install the required Simulator and iOS platform version, in this example for iOS 12.4 : com.apple.CoreSimulator.SimRuntime.iOS-12–4 and iPhoneX: com.apple.CoreSimulator.SimDeviceType.iPhone-X using xcrun which in turn is installed via xcode-install gem
  3. Make a clean slate of your react-native project, by removing package-lock, removing node_modules, clearing npm cache etc. Then install with dependencies and fix audit trail
  4. Dynamically update your iOS configurations needed for Appium desired capabilities like {IOS-DEVICE-UDID}, {IOS-DEVICE-PLATFORM-VERSION} and {IOS-DEVICE-NAME}
  5. The sample appium.ios.conf.js is
const capabilities = {maxInstances: 1,browserName: '',acceptInsecureCerts: false,appiumVersion: '1.20.0',platformName: 'iOS',udid: '{IOS-DEVICE-UDID}',platformVersion: '{IOS-DEVICE-PLATFORM-VERSION}',deviceName: '{IOS-DEVICE-NAME}',app: './ios/build/HelloWorld/Build/Products/Debug-iphonesimulator/HelloWorld.app',automationName: 'XCUITest',};exports.capabilities = capabilities;

which would become something like, depending on your setup:

const capabilities = {maxInstances: 1,browserName: '',acceptInsecureCerts: false,appiumVersion: '1.20.0',platformName: 'iOS',udid: 'B01273C2-B7D2-41F2-B189-9A26FBEE160E',platformVersion: '14.3',deviceName: 'TestiPhone11',app: './ios/build/HelloWorld/Build/Products/Debug-iphonesimulator/HelloWorld.app',automationName: 'XCUITest',};exports.capabilities = capabilities;

6. Then we need to do the necessary steps to build the iOS app in react-native like installing cocoapods, doing pod install, opening the metro bundler, and finally executing react-native run-ios. All of which are self-explanatory in the config file above. The only confusing thing could be the copying of built app from XCode folder to where Appium expects it to be found. We can configure the xcodeworkspace of the project and specify the same, but I have noticed that it’s generally not persisted on the server, rather as a client config. So, this hard copy step in the config file is what works!

A word on my test framework using webdriverIO, it surely gives flexibility over traditional ones where I have to maintain a separate process altogether running and listening to the test runner. It’s neat, it starts and shuts down once tests are completed.

This entire example can be found at https://github.com/sandeepqaops/HelloWorld

Happy iOS Mobile Test Automation, now in CircleCI with Appium! Cheers!!!

P.S: In the workshop to get similar setup for Android Emulators! Stay Tuned!!!

--

--