1. Introduction
Due to the push and the customers with little money but like to inhale fragrant goods (all require 80 ~ 100 coverage -_-), I also went through react-native-testing-library for quite a while, and also played around. Detox , but no one satisfied me yet:
- Testing-library: When testing e2e, you have to mock too much, it’s quite difficult to mock the component to behave like the real component. There are times when I just write to get% coverage but the fn of that test is mocked, so it doesn’t make much sense. The UI components are all mocked, so testing the pass and still dying is as normal as weighing the milk carton.
- Detox: playing with each simulator, the device is not clear until now it has been electronically typed.
And recently, I reread the React Native docs and recommend one more guy – Appium. Appium uses drivers like XCUITest, UIAutomator, Espresso, … Native teito (big hands: v) at the company told the drivers all over the native side to run the test, so try to play around. After a period of tinkering, it is quite satisfied.
Why use Appium and not run healthy?
Saying that, in fact, running rice is not healthy at all.
When we develop a new feature for an app, we have to test for existing features, affecting them is inevitable.
Normally, we will think as “There is a tester, then what” is it right? However, the only developer must still check through the basic cases, (test more weird cases as possible) before moving on to the tester. But maybe just writing a feature is to turn on the app to press each button, not to mention a new bike (I have a very mistaken friend for newbie and newbike =))) newbie does not know the system, lack case is of course Moreover, we humans are not good at repetitive tasks like test apps, so even if we are a senior, there will be times of confusion.
Also, in the book “The Effective Engineer” by Edmond Lau (if nothing goes wrong, it’s page 159) it says:
If you have to do something manually more than twice, then write a tool for the third time
So why don’t we let the machine do boring jobs like testing, and focus on the things that make us more interested. Let’s learn how to write automated tests for our React Native app!
Some knowledge is required
- React Native, Babel (no need to be too cool)
- Get the basics of unit test writing (with jest, or something similar)
- Typescript (optional): because in the article we will use Typescript
Hardware
- Mac / Hackintosh to build iOS or Win (Android only)
- An iOS or Android phone – say no to the integration test on the simulator: v
2. Run a small example
Suddenly stuffing up a bunch of theories while not knowing what I’m going to do, is it really boring? So let’s run a small example first: Register account
The following example uses Typescript + WebdriverIO, and has the same structure as the example on the home page of the Appium link (You can also go to the above link by visiting the Appium homepage and then clicking the Examples button), of course to go pro. then we will not code like that, I will perform refactor and convert the test environment in the following articles (if any =))).
After setting up Babel with Jest (since Jest always comes with React Native, I always use it for friendly demo), let’s write a simple test for our sign up process. Currently I only fill the text input fields, because the field select, the date picker is written quite long, so I will talk about it later.
Talking too much, where’s the code?
Yes, the code here (below has an explanation of the code)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | <span class="token keyword">import</span> <span class="token punctuation">{</span> BrowserObject <span class="token punctuation">,</span> remote <span class="token punctuation">,</span> RemoteOptions <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"webdriverio"</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> iOS <span class="token operator">:</span> RemoteOptions <span class="token operator">=</span> <span class="token punctuation">{</span> path <span class="token operator">:</span> <span class="token string">"/wd/hub"</span> <span class="token punctuation">,</span> host <span class="token operator">:</span> <span class="token string">"localhost"</span> <span class="token punctuation">,</span> port <span class="token operator">:</span> <span class="token number">4723</span> <span class="token punctuation">,</span> capabilities <span class="token operator">:</span> <span class="token punctuation">{</span> platformName <span class="token operator">:</span> <span class="token string">"iOS"</span> <span class="token punctuation">,</span> automationName <span class="token operator">:</span> <span class="token string">"XCUITest"</span> <span class="token punctuation">,</span> deviceName <span class="token operator">:</span> <span class="token string">"iPhone (2)"</span> <span class="token punctuation">,</span> platformVersion <span class="token operator">:</span> <span class="token string">"14.1"</span> <span class="token punctuation">,</span> app <span class="token operator">:</span> <span class="token string">"org.reactjs.native.example.LearnRnE2eTest"</span> <span class="token punctuation">,</span> udid <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">IOS_DEVICE_UUID</span> <span class="token punctuation">,</span> xcodeOrgId <span class="token operator">:</span> <span class="token string">"xxx"</span> <span class="token punctuation">,</span> xcodeSigningId <span class="token operator">:</span> <span class="token string">"Apple Development"</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> android <span class="token operator">:</span> RemoteOptions <span class="token operator">=</span> <span class="token punctuation">{</span> path <span class="token operator">:</span> <span class="token string">"/wd/hub"</span> <span class="token punctuation">,</span> host <span class="token operator">:</span> <span class="token string">"localhost"</span> <span class="token punctuation">,</span> port <span class="token operator">:</span> <span class="token number">4723</span> <span class="token punctuation">,</span> capabilities <span class="token operator">:</span> <span class="token punctuation">{</span> automationName <span class="token operator">:</span> <span class="token string">"UiAutomator2"</span> <span class="token punctuation">,</span> platformName <span class="token operator">:</span> <span class="token string">"android"</span> <span class="token punctuation">,</span> platformVersion <span class="token operator">:</span> <span class="token string">"8.0.0"</span> <span class="token punctuation">,</span> deviceName <span class="token operator">:</span> <span class="token string">"BH9057609A"</span> <span class="token punctuation">,</span> appPackage <span class="token operator">:</span> <span class="token string">"com.learnrne2etest"</span> <span class="token punctuation">,</span> appActivity <span class="token operator">:</span> <span class="token string">".MainActivity"</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> client <span class="token operator">:</span> BrowserObject <span class="token punctuation">;</span> <span class="token function">beforeEach</span> <span class="token punctuation">(</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> client <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">remote</span> <span class="token punctuation">(</span> iOS <span class="token punctuation">)</span> <span class="token punctuation">;</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> client <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">afterEach</span> <span class="token punctuation">(</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> client <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">deleteSession</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">scrollTo</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> <span class="token parameter">a11yId <span class="token operator">:</span> string</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> client <span class="token punctuation">.</span> options <span class="token punctuation">.</span> capabilities <span class="token punctuation">.</span> platformName <span class="token operator">===</span> <span class="token string">"android"</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">execute</span> <span class="token punctuation">(</span> <span class="token string">"mobile: scroll"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> strategy <span class="token operator">:</span> <span class="token string">"accessibility id"</span> <span class="token punctuation">,</span> selector <span class="token operator">:</span> a11yId <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token function">it</span> <span class="token punctuation">(</span> <span class="token string">"sign up the user"</span> <span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> toRegistrationScreenButton <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~login/toRegistrationScreenButton"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> toRegistrationScreenButton <span class="token punctuation">.</span> <span class="token function">click</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> registerButton <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/registerButton"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> <span class="token function">scrollTo</span> <span class="token punctuation">(</span> <span class="token string">"registration/toLoginScreenButton"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> registerButton <span class="token punctuation">.</span> <span class="token function">click</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> <span class="token function">scrollTo</span> <span class="token punctuation">(</span> <span class="token string">"registration/usernameInput-error"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> usernameError <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/usernameInput-error"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">expect</span> <span class="token punctuation">(</span> <span class="token keyword">await</span> usernameError <span class="token punctuation">.</span> <span class="token function">getText</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toMatch</span> <span class="token punctuation">(</span> <span class="token string">"Please enter username"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> passwordError <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/passwordInput-error"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">expect</span> <span class="token punctuation">(</span> <span class="token keyword">await</span> passwordError <span class="token punctuation">.</span> <span class="token function">getText</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toMatch</span> <span class="token punctuation">(</span> <span class="token string">"Please enter password"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> passwordConfirmationError <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/passwordConfirmationInput-error"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">expect</span> <span class="token punctuation">(</span> <span class="token keyword">await</span> passwordConfirmationError <span class="token punctuation">.</span> <span class="token function">getText</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toMatch</span> <span class="token punctuation">(</span> <span class="token string">"Please confirm your password"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> fullNameError <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/fullNameInput-error"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">expect</span> <span class="token punctuation">(</span> <span class="token keyword">await</span> fullNameError <span class="token punctuation">.</span> <span class="token function">getText</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toMatch</span> <span class="token punctuation">(</span> <span class="token string">"Please enter your full name"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> usernameInput <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/usernameInput"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> usernameInput <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"user..01"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> usernameError <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/usernameInput-error"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">expect</span> <span class="token punctuation">(</span> <span class="token keyword">await</span> usernameError <span class="token punctuation">.</span> <span class="token function">getText</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toMatch</span> <span class="token punctuation">(</span> <span class="token string">"Username must be alphabet and numbers"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> usernameInput <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"user"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> passwordInput <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/passwordInput"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> passwordInput <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"password"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> passwordConfirmationInput <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/passwordConfirmationInput"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> passwordConfirmationInput <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"password123"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> passwordConfirmationError <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/passwordConfirmationInput-error"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">expect</span> <span class="token punctuation">(</span> <span class="token keyword">await</span> passwordConfirmationError <span class="token punctuation">.</span> <span class="token function">getText</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toMatch</span> <span class="token punctuation">(</span> <span class="token string">"Password confirmation must match your password"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> passwordConfirmationInput <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"password"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> fullNameInput <span class="token operator">=</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registration/fullNameInput"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> fullNameInput <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"Test"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">hideKeyboard</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> <span class="token function">scrollTo</span> <span class="token punctuation">(</span> <span class="token string">"registration/toLoginScreenButton"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> registerButton <span class="token punctuation">.</span> <span class="token function">click</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> client <span class="token punctuation">.</span> <span class="token function">waitUntil</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> client <span class="token punctuation">.</span> <span class="token function">$</span> <span class="token punctuation">(</span> <span class="token string">"~registrationCompleted/toLoginScreenButton"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">element</span> <span class="token punctuation">)</span> <span class="token operator">=></span> element <span class="token punctuation">.</span> <span class="token function">isDisplayed</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> timeout <span class="token operator">:</span> <span class="token number">10000</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Details of the source code can be found here
The source code of the screens can be found here:
And here is the finished product:
1 2 3 4 5 6 7 8 9 10 11 12 | $ yarn test <span class="token constant">PASS</span> __tests__ <span class="token operator">/</span> sign <span class="token operator">-</span> up <span class="token punctuation">.</span> spec <span class="token punctuation">.</span> <span class="token function">ts</span> <span class="token punctuation">(</span> <span class="token number">18.817</span> s <span class="token punctuation">)</span> ✓ sign up the <span class="token function">user</span> <span class="token punctuation">(</span> <span class="token number">17179</span> ms <span class="token punctuation">)</span> Test Suites <span class="token operator">:</span> <span class="token number">1</span> passed <span class="token punctuation">,</span> <span class="token number">1</span> total Tests <span class="token operator">:</span> <span class="token number">1</span> passed <span class="token punctuation">,</span> <span class="token number">1</span> total Snapshots <span class="token operator">:</span> <span class="token number">0</span> total Time <span class="token operator">:</span> <span class="token number">18.869</span> s <span class="token punctuation">,</span> estimated <span class="token number">29</span> s Ran all test suites <span class="token punctuation">.</span> ✨ Done <span class="token keyword">in</span> <span class="token number">21.80</span> s <span class="token punctuation">.</span> |
iOS | Android |
---|---|
What does the above example do?
- From the Login screen, click on the button ‘Create an account’
login/toRegistrationScreenButton
to enter the Registration screen - At the Registration screen
1.1.Click the button “Register”
registration/registerButton
, because my Android device screen is a bit short, so before you right click, scroll to that element first.1.2. Do not fill anything, the expect error message displays accordingly
1.3. Fill 「user..01」 in the input username, because this field only accepts alphabet and number, so that value is not valid, expect will display error message
1.4. Fill username with a valid value 「user」
1.5. Fill password 「password」
1.6. Fill password confirmation 「password123」, because you don’t match the entered password, an error will be displayed
1.7. Fill in valid password confirmation ‘password’
1.8. Fill full name
1.9. Click on the button 「Register」
- The user has been passed through the RegistrationCompleted screen, so we expect the button on this screen to be displayed.
Analyze a few notes about the syntax
The syntax of WebdriverIO looks quite clear and easy to understand, so I will not explain much, but there are a few notes below for you.
- When searching for the element, I used
~
, this is the shorthand to find the element by theaccessibility id
of WebdriverIO, depending on the OS, we will have to assign a different prop to use the search strategy byaccessibility id
this link , just because of this. It took me a few hours, I don’t understand why I couldn’t find element =))- On iOS, we must use
testID
, and avoid assigningaccessibilityLabel
andaccessibilityHint
to components. - On the Android side, use
accessibilityLabel
- You can refer to other strategies in the docs of the webdriverio link
- On iOS, we must use
- Why is
accessibility id
without using its content?- It is true that when pressing buttons or manipulating the screen, we should use its text, so that when the text (requirement) changes, the test will be the same. But basically because it’s a
bit toolazy =)) and accessibility id is correct but the text is wrong, it must be accepted =))
- It is true that when pressing buttons or manipulating the screen, we should use its text, so that when the text (requirement) changes, the test will be the same. But basically because it’s a
- Since WebdriverIO API returns Promise, almost every command needs to use
async/await
, you can use package@wdio/sync
to make WebdriverIO API become synchronous. - Have you noticed the code in the
beforeEach
andafterEach
? For each test, we are creating a new session, and clearing that session after each test. It is more convenient to use the CLI of wdio. - The
xcodeOrgId
andxcodeSigningId
within thecapabilities
ofRemoteOptions
iOS- To run the test on an iOS device, Appium will install a software called
WebDriverAgent
on the device, and to install it, we need to provide the developer team and signing certificate.You can see more here link
- To run the test on an iOS device, Appium will install a software called
- For real devices, we need to add UUID, you can see how to find UUID here
capabilities
of Android- To get
deviceName
, runadb devices
com.learnrne2etest
and.MainActivity
can be obtained fromandroid/app/src/main/AndroidManifest.xml
1234567<span class="token tag"><span class="token tag"><span class="token punctuation"><</span> manifest</span> <span class="token attr-name">package</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> com.learnrne2etest <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span><span class="token comment"><!-- ... --></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span> activity</span> <span class="token attr-name"><span class="token namespace">android:</span> name</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> .MainActivity <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span><span class="token comment"><!-- ... --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span> activity</span> <span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span> manifest</span> <span class="token punctuation">></span></span>Or refer to the following post link
- To get
It’s enough when riding a horse to see flowers, let’s learn more about how Appium works
3. Architecture, flow of Appium
Appium is based on client-server architecture, Appium itself is a server, you can easily see this in config.
1 2 3 4 5 6 7 | <span class="token keyword">const</span> iOS <span class="token operator">:</span> RemoteOptions <span class="token operator">=</span> <span class="token punctuation">{</span> path <span class="token operator">:</span> <span class="token string">"/wd/hub"</span> <span class="token punctuation">,</span> host <span class="token operator">:</span> <span class="token string">"localhost"</span> <span class="token punctuation">,</span> port <span class="token operator">:</span> <span class="token number">4723</span> <span class="token punctuation">,</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> |
3.1. Test library + WebDriver client
When writing tests, we spend most of our time working with them, as in the example in part 2, we used Jest (with the it
, describe
, afterEach
, afterAll
) as the tools to run the test, and WebdriverIO (with $
, click()
, setValue(value)
) to communicate with Appium server.
In addition to this duo, we can use any language we like (as long as Appium it supports: v), from Ruby, Python to PHP, C #, the list can be found here.
But I recommend the following combo:
- Language: JS / TS is React Native dev friendly
- WebDriver client: WebdriverIO because among clients, this guy has the most stars on github =))
- Test library: jasmine
- Because it has the same syntax as Jest, but the React Native code siblings are too familiar with Jest
- It is one of the 3 test frameworks that are supported by WebdriverIO to go pro =)) (in addition, there are mocha and cucumber), if you use Jest, if you have an error, you have to figure it out:
As mentioned above, Appium adopts a client-server architecture. When we write the following statements
1 2 3 | <span class="token comment">// ./__tests__/sign-up.spec.ts</span> <span class="token keyword">await</span> passwordConfirmationInput <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"password123"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
If you pay attention to the log, you can see the following log lines, webdriverio simply sends an HTTP request to the server, what does the server do, we will learn the following: v
- iOS
1 2 3 4 5 6 7 8 9 10 11 | <span class="token punctuation">[</span> <span class="token constant">HTTP</span> <span class="token punctuation">]</span> <span class="token operator">--</span> <span class="token operator">></span> <span class="token constant">POST</span> <span class="token operator">/</span> wd <span class="token operator">/</span> hub <span class="token operator">/</span> session <span class="token operator">/</span> b891507e <span class="token operator">-</span> <span class="token number">4</span> d84 <span class="token operator">-</span> <span class="token number">4e62</span> <span class="token operator">-</span> a5b2 <span class="token operator">-</span> e9110c529c9e <span class="token operator">/</span> element <span class="token operator">/</span> <span class="token number">6</span> F000000 <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token constant">E00E</span> <span class="token operator">-</span> <span class="token number">000000000000</span> <span class="token operator">/</span> value <span class="token punctuation">[</span> <span class="token constant">HTTP</span> <span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token string">"text"</span> <span class="token operator">:</span> <span class="token string">"password123"</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span> debug <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token constant">W3C</span> <span class="token punctuation">(</span> b891507e <span class="token punctuation">)</span> <span class="token punctuation">]</span> Calling AppiumDriver <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">with</span> args <span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"password123"</span> <span class="token punctuation">,</span> <span class="token string">"6F000000-0000-0000-E00E-000000000000"</span> <span class="token punctuation">,</span> <span class="token string">"b891507e-4d84-4e62-a5b2-e9110c529c9e"</span> <span class="token punctuation">]</span> <span class="token punctuation">[</span> debug <span class="token punctuation">]</span> <span class="token punctuation">[</span> XCUITest <span class="token punctuation">]</span> Executing command <span class="token string">'setValue'</span> <span class="token punctuation">[</span> debug <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token constant">WD</span> Proxy <span class="token punctuation">]</span> Matched <span class="token string">'/element/6F000000-0000-0000-E00E-000000000000/value'</span> to command name <span class="token string">'setValue'</span> <span class="token punctuation">[</span> debug <span class="token punctuation">]</span> <span class="token punctuation">[</span> Protocol Converter <span class="token punctuation">]</span> Added <span class="token string">'text'</span> property <span class="token string">"password123"</span> to <span class="token string">'setValue'</span> request body <span class="token punctuation">[</span> debug <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token constant">WD</span> Proxy <span class="token punctuation">]</span> Proxying <span class="token punctuation">[</span> <span class="token constant">POST</span> <span class="token operator">/</span> element <span class="token operator">/</span> <span class="token number">6</span> F000000 <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token constant">E00E</span> <span class="token operator">-</span> <span class="token number">000000000000</span> <span class="token operator">/</span> value <span class="token punctuation">]</span> to <span class="token punctuation">[</span> <span class="token constant">POST</span> http <span class="token operator">:</span> <span class="token operator">/</span> <span class="token operator">/</span> <span class="token number">127.0</span> <span class="token number">.0</span> <span class="token number">.1</span> <span class="token operator">:</span> <span class="token number">8100</span> <span class="token operator">/</span> session <span class="token operator">/</span> <span class="token number">01</span> BFCDD1 <span class="token operator">-</span> <span class="token number">27</span> D7 <span class="token operator">-</span> <span class="token number">4904</span> <span class="token operator">-</span> <span class="token constant">A95A</span> <span class="token operator">-</span> <span class="token constant">C75086994546</span> <span class="token operator">/</span> element <span class="token operator">/</span> <span class="token number">6</span> F000000 <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token constant">E00E</span> <span class="token operator">-</span> <span class="token number">000000000000</span> <span class="token operator">/</span> value <span class="token punctuation">]</span> <span class="token keyword">with</span> body <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"value"</span> <span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"p"</span> <span class="token punctuation">,</span> <span class="token string">"a"</span> <span class="token punctuation">,</span> <span class="token string">"s"</span> <span class="token punctuation">,</span> <span class="token string">"s"</span> <span class="token punctuation">,</span> <span class="token string">"w"</span> <span class="token punctuation">,</span> <span class="token string">"o"</span> <span class="token punctuation">,</span> <span class="token string">"r"</span> <span class="token punctuation">,</span> <span class="token string">"d"</span> <span class="token punctuation">,</span> <span class="token string">"1"</span> <span class="token punctuation">,</span> <span class="token string">"2"</span> <span class="token punctuation">,</span> <span class="token string">"3"</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token string">"text"</span> <span class="token operator">:</span> <span class="token string">"password123"</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span> debug <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token constant">WD</span> Proxy <span class="token punctuation">]</span> Got response <span class="token keyword">with</span> status <span class="token number">200</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"value"</span> <span class="token operator">:</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> <span class="token string">"sessionId"</span> <span class="token operator">:</span> <span class="token string">"01BFCDD1-27D7-4904-A95A-C75086994546"</span> <span class="token punctuation">}</span> <span class="token punctuation">[</span> debug <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token constant">W3C</span> <span class="token punctuation">(</span> b891507e <span class="token punctuation">)</span> <span class="token punctuation">]</span> Responding to client <span class="token keyword">with</span> driver <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> result <span class="token operator">:</span> <span class="token keyword">null</span> <span class="token punctuation">[</span> <span class="token constant">HTTP</span> <span class="token punctuation">]</span> <span class="token operator"><</span> <span class="token operator">--</span> <span class="token constant">POST</span> <span class="token operator">/</span> wd <span class="token operator">/</span> hub <span class="token operator">/</span> session <span class="token operator">/</span> b891507e <span class="token operator">-</span> <span class="token number">4</span> d84 <span class="token operator">-</span> <span class="token number">4e62</span> <span class="token operator">-</span> a5b2 <span class="token operator">-</span> e9110c529c9e <span class="token operator">/</span> element <span class="token operator">/</span> <span class="token number">6</span> F000000 <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token constant">E00E</span> <span class="token operator">-</span> <span class="token number">000000000000</span> <span class="token operator">/</span> value <span class="token number">200</span> <span class="token number">821</span> ms <span class="token operator">-</span> <span class="token number">14</span> |
- Android
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | console <span class="token punctuation">.</span> info <span class="token number">2020</span> <span class="token operator">-</span> <span class="token number">12</span> <span class="token operator">-</span> <span class="token number">31</span> T02 <span class="token operator">:</span> <span class="token number">12</span> <span class="token operator">:</span> <span class="token number">23.288</span> Z <span class="token constant">INFO</span> webdriver <span class="token operator">:</span> <span class="token constant">COMMAND</span> <span class="token function">findElement</span> <span class="token punctuation">(</span> <span class="token string">"accessibility id"</span> <span class="token punctuation">,</span> <span class="token string">"login/toRegistrationScreenButton"</span> <span class="token punctuation">)</span> at node_modules <span class="token operator">/</span> @wdio <span class="token operator">/</span> logger <span class="token operator">/</span> build <span class="token operator">/</span> node <span class="token punctuation">.</span> js <span class="token operator">:</span> <span class="token number">76</span> <span class="token operator">:</span> <span class="token number">9</span> console <span class="token punctuation">.</span> info <span class="token number">2020</span> <span class="token operator">-</span> <span class="token number">12</span> <span class="token operator">-</span> <span class="token number">31</span> T02 <span class="token operator">:</span> <span class="token number">12</span> <span class="token operator">:</span> <span class="token number">23.289</span> Z <span class="token constant">INFO</span> webdriver <span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token constant">POST</span> <span class="token punctuation">]</span> http <span class="token operator">:</span> <span class="token operator">/</span> <span class="token operator">/</span> localhost <span class="token operator">:</span> <span class="token number">4723</span> <span class="token operator">/</span> wd <span class="token operator">/</span> hub <span class="token operator">/</span> session <span class="token operator">/</span> <span class="token number">7</span> fc4b801 <span class="token operator">-</span> deae <span class="token operator">-</span> <span class="token number">49</span> dc <span class="token operator">-</span> <span class="token number">947</span> d <span class="token operator">-</span> <span class="token number">292</span> d67ba5467 <span class="token operator">/</span> element at node_modules <span class="token operator">/</span> @wdio <span class="token operator">/</span> logger <span class="token operator">/</span> build <span class="token operator">/</span> node <span class="token punctuation">.</span> js <span class="token operator">:</span> <span class="token number">76</span> <span class="token operator">:</span> <span class="token number">9</span> console <span class="token punctuation">.</span> info <span class="token number">2020</span> <span class="token operator">-</span> <span class="token number">12</span> <span class="token operator">-</span> <span class="token number">31</span> T02 <span class="token operator">:</span> <span class="token number">12</span> <span class="token operator">:</span> <span class="token number">23.289</span> Z <span class="token constant">INFO</span> webdriver <span class="token operator">:</span> <span class="token constant">DATA</span> <span class="token punctuation">{</span> using <span class="token operator">:</span> <span class="token string">'accessibility id'</span> <span class="token punctuation">,</span> value <span class="token operator">:</span> <span class="token string">'login/toRegistrationScreenButton'</span> <span class="token punctuation">}</span> at node_modules <span class="token operator">/</span> @wdio <span class="token operator">/</span> logger <span class="token operator">/</span> build <span class="token operator">/</span> node <span class="token punctuation">.</span> js <span class="token operator">:</span> <span class="token number">76</span> <span class="token operator">:</span> <span class="token number">9</span> console <span class="token punctuation">.</span> info <span class="token number">2020</span> <span class="token operator">-</span> <span class="token number">12</span> <span class="token operator">-</span> <span class="token number">31</span> T02 <span class="token operator">:</span> <span class="token number">12</span> <span class="token operator">:</span> <span class="token number">23.414</span> Z <span class="token constant">INFO</span> webdriver <span class="token operator">:</span> <span class="token constant">RESULT</span> <span class="token punctuation">{</span> <span class="token string">'element-6066-11e4-a52e-4f735466cecf'</span> <span class="token operator">:</span> <span class="token string">'bb82f194-16d1-4ba0-a520-33f47e117211'</span> <span class="token punctuation">,</span> <span class="token constant">ELEMENT</span> <span class="token operator">:</span> <span class="token string">'bb82f194-16d1-4ba0-a520-33f47e117211'</span> <span class="token punctuation">}</span> at node_modules <span class="token operator">/</span> @wdio <span class="token operator">/</span> logger <span class="token operator">/</span> build <span class="token operator">/</span> node <span class="token punctuation">.</span> js <span class="token operator">:</span> <span class="token number">76</span> <span class="token operator">:</span> <span class="token number">9</span> |
3.2. Appium server, Automation tool, Devices
This is the backbone of Appium’s architecture, it takes care of handling requests from clients, communicating with the native automation tool to execute the commands we need.
Appium server expose API to JSON Wire Protocol (WebDriver Protocol). Depending on whether the target is iOS or Android, it will have its own way of acting to suit that platform.
3.2.1. Appium meets iOS
Going back to the log in Section 3.1, it has more or less hinted at how Appium works
1 2 3 | <span class="token punctuation">[</span> <span class="token constant">HTTP</span> <span class="token punctuation">]</span> <span class="token operator">--</span> <span class="token operator">></span> <span class="token constant">POST</span> <span class="token operator">/</span> wd <span class="token operator">/</span> hub <span class="token operator">/</span> session <span class="token operator">/</span> b891507e <span class="token operator">-</span> <span class="token number">4</span> d84 <span class="token operator">-</span> <span class="token number">4e62</span> <span class="token operator">-</span> a5b2 <span class="token operator">-</span> e9110c529c9e <span class="token operator">/</span> element <span class="token operator">/</span> <span class="token number">6</span> F000000 <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token constant">E00E</span> <span class="token operator">-</span> <span class="token number">000000000000</span> <span class="token operator">/</span> value <span class="token punctuation">[</span> debug <span class="token punctuation">]</span> <span class="token punctuation">[</span> <span class="token constant">WD</span> Proxy <span class="token punctuation">]</span> Proxying <span class="token punctuation">[</span> <span class="token constant">POST</span> <span class="token operator">/</span> element <span class="token operator">/</span> <span class="token number">6</span> F000000 <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token constant">E00E</span> <span class="token operator">-</span> <span class="token number">000000000000</span> <span class="token operator">/</span> value <span class="token punctuation">]</span> to <span class="token punctuation">[</span> <span class="token constant">POST</span> http <span class="token operator">:</span> <span class="token operator">/</span> <span class="token operator">/</span> <span class="token number">127.0</span> <span class="token number">.0</span> <span class="token number">.1</span> <span class="token operator">:</span> <span class="token number">8100</span> <span class="token operator">/</span> session <span class="token operator">/</span> <span class="token number">01</span> BFCDD1 <span class="token operator">-</span> <span class="token number">27</span> D7 <span class="token operator">-</span> <span class="token number">4904</span> <span class="token operator">-</span> <span class="token constant">A95A</span> <span class="token operator">-</span> <span class="token constant">C75086994546</span> <span class="token operator">/</span> element <span class="token operator">/</span> <span class="token number">6</span> F000000 <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token number">0000</span> <span class="token operator">-</span> <span class="token constant">E00E</span> <span class="token operator">-</span> <span class="token number">000000000000</span> <span class="token operator">/</span> value <span class="token punctuation">]</span> |
It can be seen that the Appium server simply proxies our request to another server running at port 8100 http://127.0.0.1:8100
.
Blessed are you? Who is running at port 8100?
It is WebDriverAgent server , originally developed by the big Facebook , the appium has forked to add something salt to it.
WDA helps communicate with XCUITest so you can control your iOS device / simulator remotely. It has already supported Webdriver Protocol, so surely Appium developer thought the following
What is the crime of having to re-code, have delicious products available, then use only =))
What WDA to do with the device, I would like to skip, because I don’t know =)) You can dig deeper by yourself if you are interested in it.
3.2.2. Appium meets Android
With Android, we have fewer suggestions
1 2 3 4 5 6 | <span class="token number">2020</span> <span class="token operator">-</span> <span class="token number">12</span> <span class="token operator">-</span> <span class="token number">31</span> T02 <span class="token operator">:</span> <span class="token number">12</span> <span class="token operator">:</span> <span class="token number">23.289</span> Z <span class="token constant">INFO</span> webdriver <span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token constant">POST</span> <span class="token punctuation">]</span> http <span class="token operator">:</span> <span class="token operator">/</span> <span class="token operator">/</span> localhost <span class="token operator">:</span> <span class="token number">4723</span> <span class="token operator">/</span> wd <span class="token operator">/</span> hub <span class="token operator">/</span> session <span class="token operator">/</span> <span class="token number">7</span> fc4b801 <span class="token operator">-</span> deae <span class="token operator">-</span> <span class="token number">49</span> dc <span class="token operator">-</span> <span class="token number">947</span> d <span class="token operator">-</span> <span class="token number">292</span> d67ba5467 <span class="token operator">/</span> element <span class="token number">2020</span> <span class="token operator">-</span> <span class="token number">12</span> <span class="token operator">-</span> <span class="token number">31</span> T02 <span class="token operator">:</span> <span class="token number">12</span> <span class="token operator">:</span> <span class="token number">23.414</span> Z <span class="token constant">INFO</span> webdriver <span class="token operator">:</span> <span class="token constant">RESULT</span> <span class="token punctuation">{</span> <span class="token string">'element-6066-11e4-a52e-4f735466cecf'</span> <span class="token operator">:</span> <span class="token string">'bb82f194-16d1-4ba0-a520-33f47e117211'</span> <span class="token punctuation">,</span> <span class="token constant">ELEMENT</span> <span class="token operator">:</span> <span class="token string">'bb82f194-16d1-4ba0-a520-33f47e117211'</span> <span class="token punctuation">}</span> |
Can’t see some proxy, right? That’s right, because it doesn’t have a proxy: v
In the latest version, Appium uses appium-android-driver to communicate with UIAutomator2. So how to communicate?
Every time the test starts, Appium will install Appium Settings app on our device, this guy will open port 4724 on our device and expose some system APIs. Appium abc with app server installed on (over HTTP), and it will xyz with UIAutomator2 to perform the command.
4. Conclusion
As a dev, we are not subjective, depending on the tester, but let’s test it ourselves before passing it on to them. Rice testing is a boring job, so why don’t we make it more interesting by automating it. Although there will be some tricks, or to use many rather messy commands (such as the datepicker =))), but once it is run, it will give you a feeling of being like you. pangolin =))
The e2e code of the article is just temporary code, if you want to go pro you can refer to the webdriverio ‘s appium -boilerplate on how to split the module, as well as the setup project. If I have time I will write more about refactor the temporary pile of code above to go pro =)) Now, goodbye.