Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.
This repository was archived by the owner on May 1, 2024. It is now read-only.

[Bug] AsyncCommand is not testable #825

Closed
@WebDucer

Description

@WebDucer

Description

Priviously we used DelegateCommand from Prism library but want to switch to AsyncCommands now. One of the reasons was, to be able to test easier commands with async actions behind them (most of them).

If we try to unit test our view models with AsyncCommand, the executeion fails due to hard dependecy on Device.BeginInvokeOnMainThread method.

Steps to Reproduce

  1. Create a view model with an AsyncCommand
  2. Create unit test, to test the execution of this command
  3. Test fails with exception

Expected Behavior

It should be possible to test view models, without initializing Xamarin.Forms

Actual Behavior

Basic Information

  • Version with issue: 1.0.2
  • Last known good version: unknown
  • IDE: VS 2019 16.8.4
  • Platform Target Frameworks: NUinit with on .Net Core 3.1 project

Workaround

Not using AsyncCommand!

Reproduction imagery

[Test]
public async Task ResultCommand_Returns()
{
    // Arrange
    var expected = "returned result";
    var nav = Mock.Of<ICommonNavigationService>();
    var result = new Result(expected, new byte[0], new ResultPoint[0], BarcodeFormat.QR_CODE);

    var sut = CreateViewModel(nav);

    // Act
    await sut.ResultCommand.ExecuteAsync(result);

    // Assert
    Mock.Get(nav).Verify(v => v.ToPreviousWithDataAsync(CommonConstants.NavigationKeys.QR_CODE_CONTENT, expected), Times.Once);
}

Example

Log output

System.InvalidOperationException : You must call Xamarin.Forms.Forms.Init(); prior to using this property.
   at Xamarin.Forms.Device.get_PlatformServices()
   at Xamarin.Forms.Device.BeginInvokeOnMainThread(Action action)
   at Xamarin.CommunityToolkit.ObjectModel.Internals.BaseCommand`1.RaiseCanExecuteChanged()
   at Xamarin.CommunityToolkit.ObjectModel.Internals.BaseCommand`1.set_ExecutionCount(Int32 value)
   at Xamarin.CommunityToolkit.ObjectModel.Internals.BaseAsyncCommand`2.ExecuteAsync(TExecute parameter)
   at Xamarin.Common.Tests.ViewModels.QrScanViewModelTests.ResultCommand_Returns() in C:\src\XF Libs\Xamarin.Common-69-xamarin_forms_5\tests\Xamarin.Common.Tests\ViewModels\QrScanViewModelTests.cs:line 60
   at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.GetResult()
   at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(Func`1 invoke)
   at NUnit.Framework.Internal.Commands.TestMethodCommand.RunTestMethod(TestExecutionContext context)
   at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
   at NUnit.Framework.Internal.Execution.SimpleWorkItem.<>c__DisplayClass4_0.<PerformWork>b__0()
   at NUnit.Framework.Internal.ContextUtils.<>c__DisplayClass1_0`1.<DoIsolated>b__0(Object _)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at NUnit.Framework.Internal.ContextUtils.DoIsolated(ContextCallback callback, Object state)
   at NUnit.Framework.Internal.ContextUtils.DoIsolated[T](Func`1 func)
   at NUnit.Framework.Internal.Execution.SimpleWorkItem.PerformWork()

I suppose, the following line produce the problem:

/// <summary>
/// Raises the `ICommand.CanExecuteChanged` event.
/// </summary>
public void RaiseCanExecuteChanged()
{
// Automatically marshall to the Main Thread to adhere to the way that Xamarin.Forms automatically marshalls binding events to Main Thread
Device.BeginInvokeOnMainThread(() => weakEventManager.RaiseEvent(this, EventArgs.Empty, nameof(CanExecuteChanged)));
}

Reproduction Link

Metadata

Metadata

Assignees

No one assigned

    Labels

    a/CommandsThe issue/PR is related to Command and/or AsyncCommanda/tests 🧪bugSomething isn't working. Breaky break.s/unverifiedThis issue needs verification/reproduction by a team member. PRs cannot be accepted/merged.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions