Building a Scalable Test Suite with Xamarin.UITest and Page Objects

Introduction

The act of developing an automated test suite is like an odd cousin of product-oriented software development. The skills required are just difficult enough to be out of reach of your average manual tester, and in fact, to do it well takes some serious dedication to the craft of software engineering. And yet, there are still many developers who don’t write their own tests and rely on someone else to run test suites on their work. So how can we make our tests both easier to author and easier to understand?

The BDD approach came onto the scene in a big way in 2009, and is still actively in use at many organizations, but it does require organizational coordination to make the most of this methodology. It’s not for everybody. However, imagine a test suite written procedurally with little to no reuse, filled with hard-coded query lambdas and timing values. It starts out easy but is going to be impossible to read and maintain over the long term. And unlike BDD, nobody can tell at a glance what the test does unless he or she is an engineer.

Is there a middle ground? Can we improve readability and maintainability, keep the code self-documenting, and create a bunch of composable pieces which anyone can use?

Page Objects in Xamarin.UITest

A page object is a concept. There is no page object thingy that you can instantiate or construct that Xamarin.UITest provides (although Calabash does currently provide this as a predefined framework construct).

When I think of a page object, I think of a C# POCO class that has some simple properties and several methods that model the behavior of the application under test. That’s it. This class encapsulates the queries required for your test to understand and manipulate the application.

The best part about this approach is that it can make you feel very creative. Automated testing can be very boring and repetitive, which is why software engineers try to get away with not doing it. Add a little bit of architecture work and suddenly it becomes fun!

Lastly, I will point out that just like everything else in C#, there are many valid ways to do this. Take the basic concepts and shape them into a solution that fits well with your team, design practices, and philosophy towards testing. My approach isn’t necessarily the one that will work the best for you. Later, I’ll list some common things that may need to be modified or may suit your style better.

As you begin designing your page object infrastructure, it’s important to remember a few design principles that apply to object-oriented design that can help you build great tests:

YAGNI (You Ain’t Gonna Need It): It’s easy to get carried away. Build only what you need, when you need it. Don’t try to predict the future.

SOLID: This encompasses a whole set of design principles laid out by Uncle Bob that will help you write better code no matter what you’re doing. Single Responsibility Principle and Dependency Inversion are particularly useful in tests, as is the Liskov Substitution Principle.

The Page Object Makeover, Phase 1

I’m going to show you the easiest possible way to start structuring tests as page objects.

Let’s imagine a very simple test that logs in to an app. You might start off like this:

There is really nothing wrong with this code. However, when we have 100 lines of it, it starts to become a real problem. Then when we want to reuse it, it becomes quite a nightmare if you’ve copied and pasted these 100 lines into other scenarios.

Now we’re cooking. That’s a lot better. Now anytime I need to log in to the app, I just instantiate a LoginPage and call this method. This is the most basic refactoring to page objects that you can do, and even this is going to save you a ton of time. So start here!

Enhancements

Looking at this code, you probably have some questions – and these will be answered in the next blog post. If I saw this code for the first time, I’d ask:

So every page object needs to be constructed with an IApp? It would be better if we remove that dependency. (Yes, it would).

How do I actually know what page I’m on?

How do I seamlessly handle transitions from screen to screen?

Is there a way to generically load page objects for cross-platform tests?

All of this will be addressed in the next blog post. In the meantime, I’ll let on that I like to use base pages (superclasses with protected IApp fields) and dependency injection (with Autofac) to decouple tests and page objects from their dependencies.

Conclusion

Automated testing, by its very nature, is repetitive. That login scenario might need to prefix every other test in your test suite, so why would you copy and paste it all over the place? Even worse, what happens when the login page changes – maybe an animation is added – and now you have to go back through 100 scenarios and add a wait before tapping the Sign In button. That makes automated testing suck so much more. Once you realize you have a grunt work task to repeat 100 times, it’s just not fun anymore. Wouldn’t it be better just to have all Login page related logic in one place? Hence, page objects.

This approach, and many like it, will ultimately lead to cleaner tests that are easy to maintain and read, and also prevent you needing to change things all over the place when simple behaviors in the app change.