Showing posts with label iOS. Show all posts
Showing posts with label iOS. Show all posts

Friday, September 1, 2017

iOS UI Unit Test


You may already be familiar with the concept of Unit Testing. However, usually Unit Tests are done for the logic of the application, but not so much for the visual aspects and the user interaction with the application itself. Making sure the user can interact correctly with the app is as important as what the logic of the app itself does, if not more. So how do we test this?


There are some ways to achieve this in different languages, platforms, and technologies. However, if you are developing a native iOS application, there is a simple and effective way to do it. UI unit testing.


UI unit testing is a kind of unit tests that can interact with the app as a user would, tapping on different elements and triggering gestures to ensure the flow of the app is as expected.


So let’s get started with UI unit testing, by creating our first tests and learning how to run them.



Incorporate UI unit testing to your project



First, let’s start by adding the unit tests to our project. If you are creating a new project from scratch, this is really easy to do. Actually, you don’t even have to do anything! By default, both unit tests and UI unit tests are added to a project when it is created on Xcode. You will see during the creation process that the check boxes corresponding to this tests are already marked:






If you wish to add UI unit testing for an existing project it is not much harder. Just go to File -> New -> Target. On this screen just look for “iOS UI Testing Bundle” and add it to your project. Doing so will add a new folder to your project where all UI tests will be included. Pretty simple!





Creating UI Unit Tests



UI Tests will usually be located in their own folder, which by default will be called “projectNameUITests”. Inside this folder, there should already be a file for UI tests. The class that is defined here uses XCTestCase, and should override both the “setUp” and “tearDown” functions. The first test file should come with some comments that explain what those are, but basically “setUp” is called before each test, and “tearDown” is called after each test. You use them for setting the state of the app where your tests should run and doing the cleanup afterward.


Now we come to the main part of UI Unit Test: the tests themselves. We will create a new function for each test we want to do. Note that all test functions should start with “test” since that is how they are recognized as test functions instead of just auxiliary functions.


To start with, it is recommended that you create a var to reference the app itself on the test class, since this will be used many times to retrieve screen elements. This can be done as following:


class TestExamplesUITests: XCTestCase {
   var app: XCUIApplication!
   override func setUp() {
       super.setUp()
       continueAfterFailure = false
       app = XCUIApplication()
       app.launch()
   }
}


You may notice “continueAfterFailure” in the code above. It pretty much does what it says on the tin. It is a boolean value which determines if a test should continue after it fails (and find everything that fails inside that test), or if it should stop and continue with the other tests.


Now we need to create the tests themselves. As with Unit Tests in general, we will create a certain flow for the test to follow and assert that what should happen has happened. The main difference is that, instead of testing pure logic and assert the results, we will interact with screen elements and assert that the screen is updated accordingly.


Here is a really simple example of a UI Unit Test, in which we will tap a button that is on the first screen (identified as “nextButton”) which should navigate to a second screen, and later check that a label (identified as “nextScreenLabel”) is on screen now that the navigation has been performed.


func testNavigateToNextScreen() {
       XCTAssertTrue(app.buttons["nextButton"].exists)
       XCTAssertFalse(app.staticTexts["nextScreenLabel"].exists)
       app.buttons["nextButton"].tap()
       XCTAssertTrue(app.staticTexts["nextScreenLabel"].exists)
   }


As you see, first we check that the button that we want to press exists. No purpose in continuing our test if there is no button to press, right? After that, we check that the label that should only be on the second screen does not currently exist (test should start in the first screen of the App). Then we tap the button and check again if the label is now present. If everything worked correctly, this test should succeed when we run it.


There are other events we can trigger, like gestures on the App itself. For example, to create a swipe event from the user in the app, we can call app.swipeLeft(). It is that simple!
And now let’s see how to run the test and check the results.



Running tests and checking screenshots



As you’d expect, this is not rocket science.





You just need to go to the tab shown in the image above on Xcode and you will see all tests you have created, UI or otherwise. Now hover over any of the tests (or the entire testing bundle) and a “run” icon will appear. Click on it, and there you go! Just a word of warning, UI tests take much longer to run than other Unit Tests since it has to compile the app an emulate it on real time as a user would. But that is a minor price to pay.


After all selected tests have run, you will get an icon with a tick or an x indicating which of them failed and which of them succeeded.


However, this is not where it ends. We can actually see exactly how the app looked in many steps along the way of each test. Just right click on the test you want to examine more carefully, and click on “Jump to report”. You will see something like the following:




Now you just need to click on the “eye” icon at the right of one of the steps that have it, and you will see a screenshot of the app exactly at that moment of the process. Normally screenshots are taken after each interaction, so you should be able to see everything you need.


And there you have it! Here are the basics of UI Unit Testing on iOS on Swift. You should be able to take this basic tutorial and adapt it to your needs with some minor digging to get the exact interactions you need. We also have an example on our GitHub linked down below of the test done above and one extra test using the swipe interaction. Feel free to check it!


Reference


Thursday, July 27, 2017

React Native: Using native code



In this tutorial, we will assume some basic knowledge of Swift and Java, but it is not necessary to follow along.


In my last post, we started with React Native, creating our first project and understanding the basic structure. However, we didn’t get into one of the best features of React Native: being able to easily write native code to get access to native features or simply upgrading the performance.


So we will try writing a small app which will take advantage of native code to add a new contact to the phone on either iOS or Android.

Let’s get started!

Updating from previous code (Ejecting)



First, let’s get a bad part out of the way. We can’t use native code directly if we created our app with “create-react-native-app”, at least not immediately. And that is exactly what we did in the last tutorial. However, this is easy to fix, so don’t worry! We just need to eject our previously created project and we will be good to go.


To eject your project, you will need to have react-native-cli installed, so just run the following command:
yarn global add react-native-cli
#Alternatively you could install this with npm instead if you wish with npm i -g react-native-cli

Once react-native-cli is installed, navigate to your project and run the following command to eject:
npm run eject
You will be prompted to answer some questions to guide the eject process. For this tutorial, we will choose a regular React Native project, and we will use “ContactsProject” as the name for the project.


Lastly just delete the node_modules folder and run “yarn install” and we will be all ready to continue.


One final word about ejecting your project. After doing this you won’t be able to use the QR code as explained in the other post for running the application. Instead, you will need to launch it from the console or from XCode / Android Studio. But this shouldn’t be too much of a deal while continuing your own project.

Connecting with native code



Now that we have our project ready, we can start focusing on the native part. For this tutorial, we will start with a fresh projected as created by create-react-native-app, and ejected as described above.

Connecting with iOS (Swift)



First, we will start by creating a new swift file (we will call it “ContactsManager.swift”) in your project folder that it’s inside the “ios” folder. It is recommended this is done with XCode since you will be immediately prompted to create a bridging header file (“ProjectName-Bridging-Header.h”), which we will need. If you don’t do it this way it can be added later, so don’t worry.

Inside “ContactsManager.swift” let’s add the following code:


import Foundation
import Contacts
@objc(ContactsManager)
class ContactsManager: NSObject {
 
}
We just created a new class called “ContactsManager” which will be where the logic for adding the contact will be done later. One thing to note is the “objc” modifier. React Native will communicate with Objective-C, so this is an extra step to pass Swift code to Objective-C. If you feel more comfortable with Objective-C you can code directly in Objective-C and skip this, but in this tutorial, we will continue with Swift.


Let’s add now add our function to add the new contact. I won’t go into how exactly this function works since we should concentrate more on linking the code than the adding of the contact, but it should be self-explanatory.


Add the following code inside that class:


@objc(addContact:familyName:number:)
 func addContact(name: NSString, familyName: NSString, number: NSString) -> Void {
   
   if #available(iOS 9.0, *) {
     let contact = CNMutableContact()
     
     contact.givenName = name as String
     contact.familyName = familyName as String
     
     contact.phoneNumbers = [CNLabeledValue(
       label:CNLabelPhoneNumberiPhone,
       value:CNPhoneNumber(stringValue:number as String))]
     
     // Saving the newly created contact
     let store = CNContactStore()
     let saveRequest = CNSaveRequest()
     saveRequest.add(contact, toContainerWithIdentifier:nil)
     try! store.execute(saveRequest)
     
   }
 }
We need to use “objc” in the function too. As you can see, the function itself is the same as how we would do it natively. Nothing too complex here.


Now we have all the native code that we would like to run in our React Native app, but how to do it? Well, first we need to indicate the classes and functions we would like to be accessed externally in the JavaScript. For this we will need a bridge file (“ContactsManagerBridge.m”) as follows:


#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(ContactsManager, NSObject)
RCT_EXTERN_METHOD(addContact:(NSString *)name familyName:(NSString *)familyName number:(NSString *)number)

@end
In this, we indicate the class, the methods and the parameters that will be used by our JavaScript code.


The last piece of native code we need is in the bridging header which we created at the start of this section. In that file you just need to indicate that you will be using the Bridge module with this line:


#import <React/RCTBridgeModule.h>
And we are finished with the iOS part. Depending on the version of iOS you are using you may need to modify the .plist to include permission text for using Contact, but that should be easy to fix.


So let’s return to our “App.js” where the application will start. Here, we can use the following code:



import React from 'react';
import { StyleSheet, Button, View } from 'react-native';
import { NativeModules } from 'react-native';
export default class App extends React.Component {
render() {
  return (
    <View style={styles.container}>
        <Button onPress={addContact} title="Add contact"/>
    </View>
  );
}
}
function addContact(){
  let ContactsManager = NativeModules.ContactsManager;
  ContactsManager.addContact('John', 'Doe', '(555) 424-5920');
}
const styles = StyleSheet.create({
container: {
  flex: 1,
  backgroundColor: '#fff',
  alignItems: 'center',
  justifyContent: 'center',
},
});

As you can see, this will just display a simple screen with just one button, and when that button is pressed it will call the “addContact” function. This function is what calls the native code we have been implementing until now. It brings “ContactsManager” from the native modules and calls the function we created before.


And now you are ready! Just run this project, which you can easily do from XCode, and you will see the App. After clicking the button in the middle of the App, a new contact called “John Doe” should be added to the Contacts on your phone.


Connecting with Android (Java)



First, we will start by creating a new Java file in your android project folder (we will call it “ContactsManager.java”). This is where we will write our Java code to be executed by our application.


For our example, we will create a simple class called “ContactsManager”, which will extend ReactContextBaseJavaModule. This allows us to later export this class as a native module to our React Native JavaScript. This class should also override the “getName” method, returning the name by which we will access this module on the JavaScript, as it is shown in the following code:



public class ContactsManager extends ReactContextBaseJavaModule { public ContactsManager(ReactApplicationContext reactContext) { super(reactContext); } @Override public String getName() { return "ContactsManager"; } }


Now we can add our method that we wish to call from the JavaScript. This is done exactly as we would for a native android app. The only difference is that we need to add “@ReactMethod” before the declaration.


Just add the following code to the Java file:


@ReactMethod public void addContact(String firstName, String lastName, String phoneNumber) { Context contetx = getReactApplicationContext(); String strDisplayName = firstName + " " + lastName; String strNumber = phoneNumber; ArrayList<ContentProviderOperation> cntProOper = new ArrayList<ContentProviderOperation>(); int contactIndex = cntProOper.size(); cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null).build()); cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,contactIndex) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, strDisplayName) .build()); cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID,contactIndex) .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, strNumber) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE).build()); try { ContentProviderResult[] contentProresult = null; contentProresult = contetx.getContentResolver().applyBatch(ContactsContract.AUTHORITY, cntProOper); } catch (Exception exp) { }
}
Since we have finished with preparing the module we need to work on exporting it. For this, we will create a new file (we will call it “ContactsPackage.java”) where we will package modules to be later used in our React Native app. In this, we will create a new class which implements “ReactPackage”. We could add many modules in one package, but for this example, just the one we created before is enough.


Add the following code to the new package file:


public class ContactsPackage implements ReactPackage{ @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new ContactsManager(reactContext)); return modules; } }
Lastly, we need to indicate that this package should be used in our React Native app. For this, we need to open the MainApplication.java file and add our package to the “getPackages” function. The code below shows how this would be done:


@Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new ContactsPackage() ); }
With this, we have finished doing everything on the Java side to export and use our custom native module. For this particular example, you should also add the following permission to your AndroidManifest, but this is obviously not needed for other purposes:


<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
So let’s return to our “App.js” where the application will start. Here, we can use the following code:

import React from 'react';
import { StyleSheet, Button, View } from 'react-native';
import { NativeModules } from 'react-native';
export default class App extends React.Component {
render() {
  return (
    <View style={styles.container}>
        <Button onPress={addContact} title="Add contact"/>
    </View>
  );
}
}
function addContact(){
  let ContactsManager = NativeModules.ContactsManager;
  ContactsManager.addContact('John', 'Doe', '(555) 424-5920');
}
const styles = StyleSheet.create({
container: {
  flex: 1,
  backgroundColor: '#fff',
  alignItems: 'center',
  justifyContent: 'center',
},
});
As you can see, this will just display a simple screen with just one button, and when that button is pressed it will call the “addContact” function. This function is what calls the native code we have been implementing until now. It brings “ContactsManager” from the native modules and calls the function we created before.


And now you are ready! Just run this project, which you can easily do from XCode, and you will see the App. After clicking the button in the middle of the App, a new contact called “John Doe” should be added to the Contacts on your phone.


Conclusion



As you can see, incorporating native code into your React Native application is not too complicated. You can write your native code as you normally would and just do some minor bridging inside the project to indicate which functionalities should be called from where.


You may also note (if you followed both the Android and iOS sections) that we have named the module the same in both cases. React Native plays nice enough to know exactly which module to use depending on where the code is running, so you don’t even need to handle the different use for the different platforms.
If you wish to see the example code in more detail, feel free to check our github project.