Xamarin UI Test – Testing State Management

State management, ensuring that an suspended app restarts to the same place, is one of the more challenging areas of mobile development. It’s a good idea to make sure it’s covered by your automated tests. Here’s how I managed this with Xamarin UITest…

The source code for this post is available here.

1 – Setup Tests to Run in Order

My example uses two tests, one to set the data up and one to make sure the same data is present in the restored app. The Order attribute in nunit ensures they run sequentially.

[Test, Order(1)]
public void StateTest()
{
    app = AppInitializer.StartApp(platform);
    app.Tap("Button");
    app.Tap("Button");
    app.EnterText("Text", "Some text");

    SaveAppState();
}

[Test, Order(2)]
public void StateTest2()
{
    app = AppInitializer.StartApp(platform, true);
    app.WaitForElement("Clicked 2 Times");
    app.WaitForElement(x => x.Marked("Text").Text("Some text"));
}

2 – Preserve Local Data Between Tests

In regular tests any local data will be wiped before the app is run, but here it needs to be preserved. A parameter added to the AppInitialiser controls this. This means that the local data from the first test is still in place for the second test.

public static IApp StartApp(Platform platform, bool preseveData = false)
{
    var dataMode = preseveData ? AppDataMode.DoNotClear : AppDataMode.Clear;

    if (platform == Platform.Android)
    {
        return ConfigureApp.Android
            .StartApp(dataMode);
    }
    else
    {
        return ConfigureApp.iOS
           .StartApp(dataMode);
    }
}

The documentation says that the AppDataMode attribute is only supported in test cloud, but I found it works with local tests as well.

3 – Suspend the App Programatically

When the app is stopped and restarted between tests it doesn’t get a chance to save state. To get round this the first test suspends the app programmatically. This is done via backdoor methods for each platform.

Here’s a way to suspend on iOS:

[Export("SuspendApp:")]
public void SuspendApp(NSString value)
{
    UIApplication.SharedApplication.PerformSelector(new ObjCRuntime.Selector("suspend"), null, 2.0);
}

Here’s a way to ‘suspend’ on Android (note this just triggers App.OnSleep, it doesn’t really suspend the app):

[Export("SuspendApp")]
public void SuspendApp()
{
    OnPause();
    OnStop();
}

Run the Tests!

With everything in place the last thing to do is run the tests – here’s how it looks on my machine ūüôā

Advertisements
Xamarin UI Test – Testing State Management

Running Xamarin UITest Repl Outside of Tests

The Xamarin UITest Repl is a handy little tool for inspecting and interacting with the interface for iOS and Android apps. Normally you run it by adding app.Repl() to your test code but it’s also easy to run Repl outside of a test…

Android on Windows

To run Repl on an Android device connecting to Windows you need to know:

  • The location of the Repl executable (xut-repl.exe) – C:\Users\Username\AppData\Local\Temp\uitest\repl or similar
  • The location of the UITest dll, something like – C:\Users\Username.nuget\packages\Xamarin.UITest\2.0.10\lib\Xamarin.UITest.dll
  • The identifier for the connected device – you can get this by running adb devices

To start Repl open a command prompt and change to the executable folder. Then run xut-repl.exe android {uitestdllpath} {deviceid} after replacing the relevant values. For example:

xut-repl.exe android C:\Users\Username\.nuget\packages\Xamarin.UITest\2.0.10\lib\Xamarin.UITest.dll http://notused 15337521650191

Android and iOS on macOS

On macOS it’s a bit easier. Run Repl from a test then copy the title of the Repl window. Fire up a new terminal and enter the copied command.

Here’s an iOS example from my machine:

mono /var/folders/6l/bg1gb8v13mq5lgqgnj4_pdgw0000gn/T/uitest/repl/xut-repl.exe ios ~/Code/Trailmaker.Core/Trailmaker.Core.UITest/bin/Debug/Xamarin.UITest.dll http://127.0.0.1:37265 06C7B6BD-A7F2-436A-8BF9-A911F944A2CE true

And here’s an Android example:

mono /var/folders/6l/bg1gb8v13mq5lgqgnj4_pdgw0000gn/T/uitest/repl/xut-repl.exe android ~/Code/Trailmaker.Core/Trailmaker.Core.UITest/bin/Debug/Xamarin.UITest.dll http://127.0.0.1:60360 emulator-5554 ~Library/Developer/Xamarin/android-sdk-macosx
Running Xamarin UITest Repl Outside of Tests

Xamarin – Build on iOS from Windows Command Prompt/MSBuild

A quick post to describe how to build a Xamarin iOS project from MSBuild on a Windows machine. You still need a mac available but this approach means you can create an iOS artefact from a Windows TFS build agent.

On the command line run:

msbuild YourProject.sln /p:Configuration=AppStore;Platform=iPhone /p:ServerAddress=MacIpAddress /p:ServerUser=MacLogin /p:ServerPassword=MacPassword

When this is running keep an eye on the mac as it might pop up a message box or two on the first run.

After the script completes you should have a new IPA file in the bin\iPhone\Ad-Hoc directory of your iOS project on the Windows machine.

 

Xamarin – Build on iOS from Windows Command Prompt/MSBuild

Xamarin Forms Walkthrough UI

Here’s a walkthrough interface for use in Xamarin Forms apps. With a some¬†work it’s easy to move beyond the default layouts and navigations patterns and make¬†something a bit more exciting.

The video shows it in action. You can switch between pages using the buttons or by swiping. The content for the walkthrough control is defined by a list of views; it can be anything you want. The page indicators update every time you change a page.

The view provides properties to adjust the background color, back and next text, the image used for the progress indicators, and the amount of padding used for the control area.

Implementation

If you want to know how it works you can dive into the source here. In this post I’ll just run through the key parts.

The content view XAML¬†is just a stack layout which contains two grids. The top grid expands to fill the screen and contains the child content pages when they are added. The bottom grid contains the controls to switch the page. There’s also a gesture recognizer on the whole view to pick up swipe gestures.

The key bit of code for the content view is the SetContent method. This takes a list of views and adds them into the grid. It offsets the children at intervals of a page width Рso initially they extend horizontally out to right of the screen. When the page changes it animates each of the children to shift them right or left. The other thing it does is expose the configurable options.

I’ve split a lot of the logic for which pages andbuttons are displayed out into the WalkthroughController class for a couple of reasons. Firstly it’s easier to write unit tests, and secondly it nicely separates the processing and interaction logic.

That’s pretty much it – a bit of XAML and a couple of simple classes produce a nice, configurable interface ūüôā

 

Xamarin Forms Walkthrough UI

Xamarin Forms Repeater View

Here’s a quick demo and explanation of a repeater view I made for Xamarin Forms. It simply repeats a set of controls for a collection of items that it’s bound to. A ListView does this too but isn’t always what I want. It’s surprising something like this isn’t included in the framework but making your own is fairly¬†simple.

Source code and a sample application are available here.

Here’s what it looks like in action. In this simple demo each item is represented by a Frame that contains a label and a button.

Screen Shot 2017-05-14 at 07.17.50
10 internet points if you can identify¬†what’s in the background.

Let’s look at some key points in the code. The view is based on StackLayout which will take care of positioning the items.

public class Repeater : StackLayout

There are a couple of bindable properties – ItemTemplate for the item¬†layout and ItemsSource which provides the items. Note that ItemsSource hooks an action to PropertyChanging – I’ll come back to this later on.

public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(Repeater), null);
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(Repeater), propertyChanging: ItemsSourceChanging);

Usage in XAML is similar to ListView, with an ItemTemplate, DataTemplate, and ViewCell.

<ui:Repeater x:Name="MainRepeater">
    <ui:Repeater.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Frame HasShadow="True" Margin="10">
                    <StackLayout Orientation="Horizontal">
                        <Label Text="{Binding Title}" HorizontalOptions="StartAndExpand" VerticalTextAlignment="Center"/>
                        <Button Text="Select" />
                    </StackLayout>
                </Frame>
            </ViewCell>
        </DataTemplate>
    </ui:Repeater.ItemTemplate>
</ui:Repeater>

I populate the display items whenever the related properties change, or when the binding context changes.

protected override void OnPropertyChanged(string propertyName = null)
{
    base.OnPropertyChanged(propertyName);

    if (propertyName == ItemTemplateProperty.PropertyName || propertyName == ItemsSourceProperty.PropertyName)
    {
        this.Populate();
    }
}

protected override void OnBindingContextChanged()
{
    base.OnBindingContextChanged();
    this.Populate();
}

The Populate method clears out the layout, then populates the template for each item and adds it in.

public void Populate()
{
    if (this.ItemsSource != null)
    {
        this.Children.Clear();

        foreach (var item in this.ItemsSource)
        {
            var content = this.ItemTemplate.CreateContent();
            var viewCell = content as ViewCell;

            if (viewCell != null)
            {
                this.Children.Add(viewCell.View);
                viewCell.BindingContext = item;
            }
        }
    }
}

If we just wanted to bind to an IEnumerable we wouldn’t need anything more. However when binding to an ObservableCollection the list wouldn’t update when items are added or removed. ObservableCollection fires OnCollectionChanged, not OnPropertyChanged, when the list is updated. This is where the ItemsSourceChanging method we hooked up in the binding is used.

private static void ItemsSourceChanging(BindableObject bindable, object oldValue, object newValue)
{
    if (oldValue != null && oldValue is INotifyCollectionChanged)
    {
        ((INotifyCollectionChanged)oldValue).CollectionChanged -= ((Repeater)bindable).OnCollectionChanged;
    }

    if (newValue != null && newValue is INotifyCollectionChanged)
    {
        ((INotifyCollectionChanged)newValue).CollectionChanged += ((Repeater)bindable).OnCollectionChanged;
    }
}

private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
	Populate();
}

When the ItemsSource is changing we check to see if it implements INotifyCollectionChanged, if so we subscribe to the OnCollectionChanged event and populate the list when it fires. We also need to make sure we unsubscribe if the items source changes.

So that’s it, a simple alternative to ListView for repeating a set of controls.

Xamarin Forms Repeater View

macOs Folders Appear As Files

Here’s a weird macOS issue I’ve just run into while doing some cross platform development with Xamarin. If you have a folder which has a dot in it’s name it finder will sometimes think it’s a file. For me this was when the name ended in .workflow…

Screen Shot 2017-05-04 at 20.37.31
Looks like a file…
Screen Shot 2017-05-04 at 20.38.35
…but it’s really a folder

This happens when the element¬†after the last dot is recognised as a file extension. After some head scratching I found an easy solution – right click on the folder and select ‘Show Package Contents’

Screen Shot 2017-05-04 at 20.42.06
‘Show Package Contents’
Screen Shot 2017-05-04 at 20.54.22
As if by magic some files appear!
macOs Folders Appear As Files

Xamarin Forms Flow Layout

Xamarin Forms is missing a flow layout control so I made one! It’s available¬†here. It lays out child views from right to left, and moves on to a new row when it runs out of space.

Here’s a screenshot of a demo app…

Screen Shot 2017-03-22 at 20.45.36

This is the first layout I’ve made and I was surprised how easy it was. Layouts are written in the cross platform code; there isn’t a platform specific element.

To create a layout you create a new class that inherits from Layout…

public class FlowLayout : Layout<View>

The override the LayoutChildren and OnMeasure methods…

protected override void LayoutChildren(double x, double y, double width, double height)
{
    var layoutInfo = new LayoutInfo(Spacing);
    layoutInfo.ProcessLayout(Children, width);

    for (int i = 0; i < layoutInfo.Bounds.Count; i++)
    {
        if (!Children[i].IsVisible)
        {
            continue;
        }

        var bounds = layoutInfo.Bounds[i];
        bounds.Left += x;
        bounds.Top += y;

        LayoutChildIntoBoundingRegion(Children[i], bounds);
    }
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
    var layoutInfo = new LayoutInfo(Spacing);
    layoutInfo.ProcessLayout(Children, widthConstraint);
    return new SizeRequest(new Size(widthConstraint, layoutInfo.HeightRequest));
}

I chose to use another class – LayoutInfo – to handle the layout. This meant I was able to easily write unit tests. Trying to unit test the actual layout class is difficult as it needs the Xamarin Forms framework to work in the test environment.

 

Xamarin Forms Flow Layout