We have ushered in a Mobile era in 2017. As there were more mobile users in 2016 then desktop users across the globe. All things including services, products, and even supplies will need to accommodate to hand-held devices like mobiles, tablets and phablets. It will become a game of survival of sorts where ‘go mobile or perish’ will be the condition. Else, you can bid Adieu to your customers and business as they migrate to mobile apps and mobile friendly websites.
Building mobile applications can be challenging. Besides working on your business and product, you have to specifically think about the User Journey, User experience, User Interface, the infrastructure and engineering. You also need to make sure that the apps are working properly before you launch and as you know testing an iOS applications is different from testing an Android application. We have had quite a lot of frustrating experiences when it comes to iOS Testing and in this article we will talk about how we used progressive methods to solve these issues.
User Interface testing
The problem with Apple’s UI Automation, introduced by Xcode 7, is that it separated its processes from the main application. It was impossible to stub network requests using modern methods. When researching, we found that many experts suggested to use the following alternative solutions to our problems:
- Mix the production code with testing code which would make it easier to find the paired sources or;
- Add an entire mocking framework (e.g: OHHTTPStubs) to the applications code. This practice allows you to stub your network requests easily. You can test the app with fake network data and custom response time, response code and headers.
These alternative solutions however, were very confusing to implement properly and resulted in coding spaghetti.
After thoroughly researching, we found that there are 2 progressive ways to solve our problems better:
- Using KIF - (Keep It Functional) an iOS integration test framework, designed for Objective-C, which also works well in Swift projects. It runs on a Unit Test target, which runs together with the main application as a unique structure allowing the network stub to work smoothly.
- Attaching a mini HTTP server to UI Automation tests. This allows the main application to make real network requests. We used the HTTP server to config and pointed to our test server.
We preferred HTTP over KIF because it is pure Swift and used more by developers within the community.
Using Swift brought us some problems with Apple’s Unit Testing. Apple describes this new language as follows: “Swift adopts safe programming patterns and adds modern features to make programming easier, more flexible, and more fun. Swift’s clean slate, backed by the mature and much-loved Cocoa and Cocoa Touch frameworks, is an opportunity to reimagine how software development works.” However, as the language was designed to be safe, proper readwrite reflection was not yet supported and there was no way to modify your program at runtime. This posed a major obstacle for us in Apple Unit Testing.
Few years ago, we mainly relied on UI testing as mocking was difficult to execute in unit testing. As Orosz, an engineering manager with experience leading web, mobile and backend teams, stated: “When writing unit tests, the fundamentals of verifying correct behaviour is isolation”. One common practice of isolating components is mocking. However, since Swift language does not support read write reflection, we were not able to implement the proper mocking. Even though 90% code coverage was generated using UI testing, this was not a solid testing base that we could totally rely on. There were still many strategies that were still insufficiently tested.
To deal with the mocking issue, we adopted Dependency Injection practice. This design pattern makes testing possible without the use of any framework at all, which is also compatible with Swift. Dependency Injection has made Unit Testing become more fluent thanks to its versatility to replicate the behaviour of the original class.
SOLID principles (Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion) can also be an element that make iOS unit testing much easier. Following this principle was a way to write clean services that fully support tests. Besides, The Clean Architecture was applied as the main architectural pattern so our project could scale to any level while the modules were decoupling. This helped to prevent the overloading of the system due to the increasing number of third-party services being added to the project. Many people claim that The Clean Architecture is only suitable for a big project because it is complicated and it breaks the project structure into many different layers. However, with our experience, we can confirm that it works for all projects and on any scale.
Testing is not something complicated and tricky by itself. It just requires a bit of discipline and some practice. Through continuous experimenting, we finally were able to find proper solutions to iOS testing. Would you like to learn more about how we can implement progressive solutions to your software issues? Contact Us to learn more about the services and solutions that East Agile has for you.