DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Practical Use of Weak Symbols
  • Generate Unit Tests With AI Using Ollama and Spring Boot
  • Understanding the Two Schools of Unit Testing
  • Go: Unit and Integration Tests

Trending

  • Performance Optimization Techniques for Snowflake on AWS
  • Build an MCP Server Using Go to Connect AI Agents With Databases
  • Accelerating AI Inference With TensorRT
  • AI's Dilemma: When to Retrain and When to Unlearn?
  1. DZone
  2. Coding
  3. Frameworks
  4. Unit Testing Xamarin Forms View Model

Unit Testing Xamarin Forms View Model

By 
Patrick Tshibanda user avatar
Patrick Tshibanda
·
Apr. 28, 20 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
8.5K Views

Join the DZone community and get the full member experience.

Join For Free
In this tutorial, we are going to see how to unit test a view model in a Xamarin Forms application.

View Model in a Nutshell

The view model is the centerpiece of the Model-View-ViewModel pattern. When you're not using MVVM, your presentation logic is in the view (code behind), making harder to test because the logic is coupled to UI elements. On the other hand, when using MVVM, your presentation logic move into the view model, by decoupling logic from the view, you gain the ability to test logic without being bothered by UI elements.

Getting Started

To walk you through this tutorial, I created a Xamarin Forms app called WorkoutTube.


WorkoutTube allows users to


  • View a list of workout videos from Youtube.
  • Launch the Youtube app with the selected video, if the Youtube app is not installed, it will launch the browser version.

The project can be downloaded here.

Project Overview



The project is already implemented, we will focus on unit tests only.

In order to run the project, edit the file AppConfiguration.cs, under the WorkoutTube project, update the property YoutubeApiKey with your own, you can request Youtube API key here.

Tools

To make testing easier, the unit tests project WorkoutTube.UnitTests is using the following packages :

  • Autofac: IoC container.
  • AutoMock: allows you to automatically create mock dependencies
  • AutoFixture: library for .NET designed to minimize the 'Arrange' phase of your unit tests
  • FluentAssertions: Fluent API for asserting the results of unit tests

Making the View Model Testable

The HomePageViewModel holds dependencies to other services, instead of relying on the concrete implementation of those services, it relies on their abstractions.

Concrete implementation will be injected through an Ioc container.

C#
 




xxxxxxxxxx
1
10


 
1
public HomePageViewModel(
2
        IVideoService videoService, 
3
        IDialogService dialogService, 
4
        IYoutubeLauncherService youtubeLauncherService) 
5
        { 
6
            _videoService = videoService; 
7
            _dialogService = dialogService; 
8
            _youtubeLauncherService = youtubeLauncherService; 
9
            ... 
10
        }


Having the ability to inject dependencies is great because when testing we can supply mock objects instead of real ones.

Dependency Injection

The WorkoutTube project uses AutoFac as Ioc container, dependencies are registered in App.xaml.cs.

C#
 




x
12


 
1
public void RegisterDependencies()
2
{
3
    var builder = new ContainerBuilder();
4

           
5
    builder.RegisterType<VideoService>().As<IVideoService>().SingleInstance();
6
    builder.RegisterType<AppConfiguration>().As<IAppConfiguration>().SingleInstance();
7
    builder.RegisterType<DialogService>().As<IDialogService>().SingleInstance();
8
    builder.RegisterType<YoutubeLauncherService>().As<IYoutubeLauncherService>().SingleInstance();
9
    builder.RegisterType<HomePageViewModel>();
10

           
11
    Container = builder.Build();
12
}


Writing Unit Tests

For our tests, the HomePageViewModel will be the subject under the test, we will focus on two types of testing :

  • State: checking how the view model's property values are affected by certain actions.
  • Interactions: checking that the view model's dependencies service's methods are called properly.

Testing States

The HomePageViewModel exposes properties and commands to which view HomePage can bind to.


Let's write a test that checks that when the HomePageViewModel is created, Videos property is empty.

C#
 




x
10


 
1
[Test]
2
public void CreatingHomePageViewModel_WhenSuccessfull_VideosIsEmpty()
3
{
4
      using (var mock = AutoMock.GetLoose())
5
      {
6
          var viewModel = mock.Create<HomePageViewModel>();
7

           
8
          viewModel.Videos.Should().BeEmpty();
9
      }
10
}


We can also write a test to check that when HomePageViewModel is created, SelectedVideo property is null, meaning there is no video selected yet.

C#
 




x
10


 
1
[Test]
2
public void CreatingHomePageViewModel_WhenSuccessfull_SelectedVideoIsNull()
3
{
4
        using (var mock = AutoMock.GetLoose())
5
        {
6
            var viewModel = mock.Create<HomePageViewModel>();
7

           
8
            viewModel.SelectedVideo.Should().BeNull();
9
        }
10
}


How about checking if the OpenVideoCommand is initialized properly.

C#
 




x
11


 
1
[Test]
2
public void CreatingHomePageViewModel_WhenSuccessfull_InitializesOpenVideoCommand()
3
{
4
    using (var mock = AutoMock.GetLoose())
5
    {
6
        var viewModel = mock.Create<HomePageViewModel>();
7

           
8
        viewModel.OpenVideoCommand.Should().NotBeNull();
9
        viewModel.OpenVideoCommand.Should().BeOfType<Command<VideoItem>>();
10
    }
11
}


The HomePageViewModel has a method called Initialize, which is called after the view model is created, in this method, the VideoService fetch videos from Youtube API.

Let's write a test to check the following scenario: after a successful initialization, Videos property should be updated with data retrieved from the service.

C#
 




x
16


 
1
[Test]
2
public async Task InitializingHomePageViewModel_WhenSuccessfull_UpdatesVideos()
3
{
4
    using (var mock = AutoMock.GetLoose())
5
    {
6
        var videos = _fixture.CreateMany<VideoItem>(5);
7
        mock.Mock<IVideoService>().Setup(v => v.GetVideoItems()).Returns(Task.FromResult(videos));
8

           
9
        var viewModel = mock.Create<HomePageViewModel>();
10
        await viewModel.Initialize();
11

           
12
        viewModel.Videos.Should().NotBeEmpty();
13
        viewModel.Videos.Count.Should().Equals(videos.Count());
14
    }
15
}
16

           


Note: notice that we are configuring IVideoService as a mock object, that way we're not communicating with the Youtube API, unit testing is not about calling real API, that's the job of integration testing.

When testing, it is important to not only test the happy path but also the sad path (when things go wrong). In HomePageViewModel, the code that loads the videos is wrapped around a try/catch block, when we catch an exception we display the message by using IDialogService.

C#
 




x


 
1
try
2
{
3
    Videos.ReplaceRange(await _videoService.GetVideoItems());
4
}
5
catch (Exception ex)
6
{
7
    await _dialogService.Alert(ex.Message);
8
}


Let's write a test for the sad path.

C#
 




x
13


 
1
[Test]
2
public async Task InitializingHomePageViewModel_WhenExceptionIsRaised_CallsDialogServiceAlert()
3
{
4
    using (var mock = AutoMock.GetLoose())
5
    {
6
        mock.Mock<IVideoService>().Setup(v => v.GetVideoItems()).Throws(new System.Exception("Unexpected error"));
7

           
8
        var viewModel = mock.Create<HomePageViewModel>();
9
        await viewModel.Initialize();
10

           
11
        mock.Mock<IDialogService>().Verify(d => d.Alert(It.IsAny<string>()));
12
    }
13
}


Testing Interactions

The HomePageViewModel exposes a command called OpenVideoCommand, when executed, it launches the Youtube app through a service called YoutubeServiceLauncher.

Let's write a test that checks that the method OpenAsync of YoutubeServiceLauncher has been called.

C#
 




x
14


 
1
[Test]
2
public void ExecutingOpenVideoCommand_WhenSuccessfull_CallsYoutubeLauncherServiceOpenAsync()
3
{
4
    using (var mock = AutoMock.GetLoose())
5
    {
6
        var video = _fixture.Create<VideoItem>();
7
        mock.Mock<IYoutubeLauncherService>().Setup(y => y.OpenAsync(video.Id.VideoId));
8

           
9
        var viewModel = mock.Create<HomePageViewModel>();
10
        viewModel.OpenVideoCommand.Execute(video);
11

           
12
        mock.Mock<IYoutubeLauncherService>().Verify(y => y.OpenAsync(video.Id.VideoId));
13
    }
14
}


 

We can also write the test for the sad path.

C#
 




x
14


 
1
[Test]
2
public void ExecutingOpenVideoCommand_WhenExceptionIsRaised_CallsDialogServiceAlert()
3
{
4
    using (var mock = AutoMock.GetLoose())
5
    {
6
        var video = _fixture.Create<VideoItem>();
7
        mock.Mock<IYoutubeLauncherService>().Setup(v => v.OpenAsync(video.Id.VideoId)).Throws(new System.Exception("Unexpected error"));
8

           
9
        var viewModel = mock.Create<HomePageViewModel>();
10
        viewModel.OpenVideoCommand.Execute(video);
11

           
12
        mock.Mock<IDialogService>().Verify(d => d.Alert(It.IsAny<string>()));
13
    }
14
}


Conclusion

When using MVVM, it is important to unit tests your view models, testing will increase the overall quality of your application.

I hope you enjoy this tutorial, the source code is available for download here. feel free to use it.

unit test View model xamarin.forms xamarin

Published at DZone with permission of Patrick Tshibanda. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Practical Use of Weak Symbols
  • Generate Unit Tests With AI Using Ollama and Spring Boot
  • Understanding the Two Schools of Unit Testing
  • Go: Unit and Integration Tests

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!