Dispatcher.Invoke hangs in unittests when Run All

Topics: Issues / bugs
Dec 12, 2012 at 3:02 PM
Edited Dec 12, 2012 at 3:11 PM

My brain will blow up in a few minutes, so i will try to describe my problem as fast as can.

I have a viewmodel SalesViewModel with properties BindingList<BasketViewModel> and ICollectionView for this collection. I'm trying to add few BasketViewModel from another thread (remote server sending me info). As expected i have exception if i try to add objects from that thread.

Ok, i wrote code to fix this problem: 

public SalesViewModel()
{
    Baskets = new BindingList<BasketViewModel>();
    BasketsView = CollectionViewSource.GetDefaultView(Baskets);
    m_Dispatcher = Catel.Windows.Threading.DispatcherHelper.CurrentDispatcher;
}

 

private void AddNewBasketViewModel(IBasket basket)
{
    m_Dispatcher.Invoke(new Action(delegate
    {
        Baskets.Add(m_ViewModelsFactory.CreateBasketViewModel(basket));
    }));
}

This works fine. I have few UnitTests.
[TestMethod]
public void CheckPropertyChangeFromModelToVm()
{
    // Менеджер продаж Gk
    IGkSaleManager gkSaleManager = IoC.Instance.ResolveType<IGkSaleManager>();

    // Добавить одну корзину
    gkSaleManager.CreateNewBasket();

    // Создать SalesViewModel
    IViewModelsFactory vmFactory = IoC.Instance.ResolveType<IViewModelsFactory>();
    var sales = vmFactory.CreateSalesViewModel();
    Assert.AreEqual(1, sales.Baskets.Count);
}
 
/// <summary>
/// Тест отслеживает изменение выбора корзины
/// </summary>
[TestMethod]
public void SelectBasketViewModelInBasketsList()
{
    IGkSaleManager saleManager = IoC.Instance.ResolveType<IGkSaleManager>();
    Assert.AreEqual<uint>(0, saleManager.GetBasketsCount());

    saleManager.CreateNewBasket();
    saleManager.CreateNewBasket();
    saleManager.CreateNewBasket();
    Assert.AreEqual<uint>(3, saleManager.GetBasketsCount());

    IViewModelsFactory vmFactory = IoC.Instance.ResolveType<IViewModelsFactory>();

    var sales = vmFactory.CreateSalesViewModel();
    Assert.AreEqual(3, sales.Baskets.Count);
    sales.BasketsView.MoveCurrentToPosition(0);
}

This is how i create and run tests. SalesViewModel created for every test. IGkSaleManager created once.
  
public class TestingViewModelsFactory : IViewModelsFactory
{
    private ISaleManagerFactory m_SaleManagerFactory = IoC.Instance.ResolveType<ISaleManagerFactory>();
    private readonly IBasketFactory m_BasketFactory = IoC.Instance.ResolveType<IBasketFactory>();
    private readonly IGoodsCatalog m_GoodsCatalog = IoC.Instance.ResolveType<IGoodsCatalog>();

    public SalesViewModel CreateSalesViewModel()
    {
        return new SalesViewModel(m_SaleManagerFactory.CreateSaleManager(), this);
    }
...

The problem is when i'm running single test - everything works fine. But when i'm using Run All - second test just hang on this Invoke(). And i see another problem - Invoke() is sync method. When i will run program UI will freeze. Another question - how to test class when using BeginInvoke()? Values will not change when assert will checked.

Coordinator
Dec 12, 2012 at 3:39 PM

You should use the IDispatcherService. It checks whether a dispatcher actually exists. If not, you will not have threading issues and it will directly invoke without using a dispatcher. This will also make sure that when running the VM inside a real app, the stuff is correctly dispatched.

Dec 13, 2012 at 6:44 AM
Edited Dec 13, 2012 at 6:57 AM

Not helped. I have repro for my problem with small project.

https://www.dropbox.com/s/nt88w2f7nz04ag6/TestProject.zip

private void AddNewBasketViewModel(IBasket basket)
{
    GetService<IDispatcherService>().Invoke(() => Baskets.Add(m_ViewModelsFactory.CreateBasketViewModel(basket)));
}
Coordinator
Dec 13, 2012 at 8:12 AM

Thanks, I will look into this. You can always create a workitem (at the right of this discussion), then you can attach files.