So I'm writing unit tests for one of my view models in Xamarin mobile app. The method I'm testing looks like this:
public async Task RefreshItems()
{
var departamentsObservable = _dataService.GetDepartaments();
departamentsObservable.ObserveOn(SynchronizationContext.Current).Subscribe(items =>
{
Departaments.ReplaceWithRange(items);
});
await departamentsObservable.FirstOrDefaultAsync();
}
_dataService.GetDepartaments();
method returns IObservable<IEnumerable<Departament>>
.
I use Observable
and Subscribe
instead of simple method that returns Task<IEnumerable<Departament>>
because in my case Observable
will "return" twice (once with data from cache and other time with newly fetched data form web).
For testing I of course mock _dataService.GetDepartaments();
method like that:
public IObservable<IEnumerable<Departament>> GetDepartaments()
{
return Observable.Return(MockData.Departaments);
}
So method returns mock data immediately.
And my test for RefreshItems
method looks like that:
[Fact]
public async Task RefreshItemsTest()
{
await _viewModel.RefreshItems();
Assert.Equal(MockData.Departaments, _viewModel.Departaments,
new DepartamentComparer());
}
The problem is that this test randomly fails (1 in 10 times approximately). Basically The Departaments
collection in view model that should be updated when Observable
"returns" is empty.
I should add that I'm using xUnit 2.1.0 test framework and xUnit console runner in Xamarin Studio.
EDIT: The Enigmativity's suggestion throws Sequence contains no elements exception only when running in test runner. Below is minimal woking example code to demonstrate the issue:
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Reactive.Linq;
using System.Threading;
using System.Collections.Generic;
namespace TestApp
{
public class TestViewModel
{
public ObservableCollection<TestDepartament> Departaments { get; set; }
private ITestDataService _dataService;
public TestViewModel(ITestDataService dataService)
{
_dataService = dataService;
Departaments = new ObservableCollection<TestDepartament>();
}
public async Task RefreshItems()
{
var facultiesObservable = _dataService.GetDepartaments();
await facultiesObservable.ObserveOn(SynchronizationContext.Current).Do(items =>
{
Departaments.Clear();
foreach(var item in items)
Departaments.Add(item);
});
}
}
public interface ITestDataService
{
IObservable<IEnumerable<TestDepartament>> GetDepartaments();
}
public class MockDataService : ITestDataService
{
public IObservable<IEnumerable<TestDepartament>> GetDepartaments()
{
return Observable.Return(TestMockData.Departaments);
}
}
public static class TestMockData
{
public static List<TestDepartament> Departaments
{
get
{
var departaments = new List<TestDepartament>();
for (int i = 0; i < 15; i++)
{
departaments.Add(new TestDepartament
{
Name = $"Departament {i}",
ImageUrl = $"departament_{i}_image_url",
ContentUrl = $"departament_{i}_content_url",
});
}
return departaments;
}
}
}
public class TestDepartament
{
public string ContentUrl { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
}
}
And this is xUnit test:
public class DepartamentsViewModelTests
{
private readonly TestViewModel _viewModel;
public DepartamentsViewModelTests()
{
var dataService = new MockDataService();
_viewModel = new TestViewModel(dataService);
}
[Fact]
public async Task RefreshItemsTest()
{
await _viewModel.RefreshItems();
Assert.Equal(TestMockData.Departaments, _viewModel.Departaments);
}
}