How to unit test RelayCommand that Executes an async method? - c#

As there is no RelayCommandAsync (at least not that I know of), how to test this scenario. For example:
public RelayCommand LoadJobCommand
{
get
{
return this.loadJobCommand ?? (
this.loadJobCommand =
new RelayCommand(
this.ExecuteLoadJobCommandAsync));
}
}
private async void ExecuteLoadJobCommandAsync()
{
await GetData(...);
}
Test:
vm.LoadJobCommand.Execute()
Assert.IsTrue(vm.Jobs.Count > 0)

It really depends on what you are trying to test:
Test that the RelayCommand is properly hooked up and calls your async
method?
or
Test that the Async Method logic is correct?
1. Testing the RelayCommand trigger
1.a Using External Dependencies to verify
From my personal experience the easiest way to test that the trigger is wired up correctly to execute the command and then test that your class has interacted with another external class somewhere as expected. E.g.
private async void ExecuteLoadJobCommandAsync()
{
await GetData(...);
}
private async void GetData(...)
{
var data = await _repo.GetData();
Jobs.Add(data);
}
Its fairly easy to test that your repo gets called.
public void TestUsingExternalDependency()
{
_repo.Setup(r => r.GetData())
.Returns(Task.Run(() => 5))
.Verifiable();
_vm.LoadJobCommand.Execute(null);
_repo.VerifyAll();
}
I sometimes even do this, so that it doesn't try to process everything:
[Test]
public void TestUsingExternalDependency()
{
_repo.Setup(r => r.GetData())
.Returns(() => { throw new Exception("TEST"); })
.Verifiable();
try
{
_vm.LoadJobCommand.Execute(null);
}
catch (Exception e)
{
e.Message.Should().Be("TEST");
}
_repo.VerifyAll();
}
1.b Using a Scheduler
Another option is to use a scheduler, and schedule tasks using that.
public interface IScheduler
{
void Execute(Action action);
}
// Injected when not under test
public class ThreadPoolScheduler : IScheduler
{
public void Execute(Action action)
{
Task.Run(action);
}
}
// Used for testing
public class ImmediateScheduler : IScheduler
{
public void Execute(Action action)
{
action();
}
}
Then in your ViewModel
public ViewModelUnderTest(IRepository repo, IScheduler scheduler)
{
_repo = repo;
_scheduler = scheduler;
LoadJobCommand = new RelayCommand(ExecuteLoadJobCommandAsync);
}
private void ExecuteLoadJobCommandAsync()
{
_scheduler.Execute(GetData);
}
private void GetData()
{
var a = _repo.GetData().Result;
Jobs.Add(a);
}
And your test
[Test]
public void TestUsingScheduler()
{
_repo.Setup(r => r.GetData()).Returns(Task.Run(() => 2));
_vm = new ViewModelUnderTest(_repo.Object, new ImmediateScheduler());
_vm.LoadJobCommand.Execute(null);
_vm.Jobs.Should().NotBeEmpty();
}
2. Testing the GetData Logic
If you are looking to test get GetData() logic or even the ExecuteLoadJobCommandAsync() logic. Then you should definitely make the method you want to test, as Internal, and mark your assmebly as InternalsVisibleTo so that you can call those methods directly from your test class.

Why don't you cover GetData(...) method with tests? I don't see any sense in testing relay commands

I was not using async/await but I have run in to a similar problem in the past. The situation I was in is the method called a Task.Run( inside of itself and the unit test was verifying that the ViewModel was calling the service with the correct number of times with the correct parameters.
The way we solved this was we had our Mock of the service that was being called use a ManualResetEventSlim, then the unit test waited for that reset event to be called before proceeding.
[TestMethod]
public void EXAMPLE()
{
using (var container = new UnityAutoMoqContainer())
{
//(SNIP)
var serviceMock = container.GetMock<ITreatmentPlanService>();
var resetEvent = new ManualResetEventSlim();
serviceMock.Setup(x=>x.GetSinglePatientViewTable(dateWindow, currentPatient, false))
.Returns(() =>
{
resetEvent.Set();
return new ObservableCollection<SinglePatientViewDataRow>();
});
var viewModel = container.Resolve<SinglePatientViewModel>();
//(SNIP)
viewModel.PatientsHadTPClosed(guids, Guid.NewGuid());
waited = resetEvent.Wait(timeout);
if(!waited)
Assert.Fail("GetSinglePatientViewTable was not called within the timeout of {0} ms", timeout);
//(SNIP)
serviceMock.Verify(x => x.GetSinglePatientViewTable(dateWindow, currentPatient, false), Times.Once);
}
}
If this approach works or not for you all depends on what your unit test is actually testing. Because you check Assert.IsTrue(vm.Jobs.Count > 0) it looks like you have extra logic that is being done after the await GetData(...); call, so this might not be applicable for your current problem. However, this may be helpful for other unit tests you need to write for your view model.

Related

Error in Azure Function Xunit Mock test : Expected invocation on the mock exactly 1 times, but was 0 times

I created a azure function with the time trigger code below. Im very new to azure function and xunit. I created both with help of some blogs. I wrote a simple unit test using Xunit in C#. but it returns an error. I tried to solve the issue and not work for me.. Please help me
public class DeleteJob
{
private readonly IStore _store;
public DeleteJob(IStore store, ILogger<DeleteJob> log)
{
_store = store;
_log = log;
}
[Function("DeleteJob")]
public async Task Run([TimerTrigger("0 */5 * * * *", RunOnStartup = false)] MyInfo myTimer)
{
var canceltoken = new CancellationTokenSource(TimeSpan.FromMinutes(8));
var deleteDate = DateTime.UtcNow.AddMonths(-6);
try
{
await DeleteBlobMetadata(deleteDate, canceltoken.Token);
}
catch (OperationCanceledException)
{
_log.LogInformation("Function ran out of time");
}
}
private async Task DeleteBlobMetadata(DateTime deleteDate, CancellationToken canceltoken)
{
try
{
if (cancellationToken.IsCancellationRequested)
return;
var BlobUrls = await _store.GetBlobBeforeDate(Constants.ContainerName, deleteDate);
foreach (var blobName in BlobUrls)
{
if (cancellationToken.IsCancellationRequested)
return;
await _store.DeleteBlobAsync(Constants.ContainerName, blobName);
}
}
catch (Exception e)
{
_log.LogError($"Exception when deleting attachments: \n{e}");
}
Following is unittest
public class DeleteTest
{
private readonly Mock<IStore> _StoreMock;
private Mock<ILogger<DeleteJob>> _logMock;
public DeleteTest()
{
_StoreMock = new Mock<IStore>();
_logMock = new Mock<ILogger<DeleteJob>>();
}
[Fact]
public async Task DeleteBlobOlderThan6Months_ShouldDelete()
{
SetupDeletionSuccessful(true);
SetupDeleteBlobSetup();
var sut = GetSut();
await sut.Run(myTimer: null);
_StoreMock.Verify(m => m.GetBlobBeforeDate(It.IsAny<string>(),It.IsAny<DateTime>()), Times.Exactly(1));
_StoreMock.Verify(m => m.DeleteAttachmentAsync(It.IsAny<string>(), It.IsAny<string>()), Times.Exactly(1));
}
private void SetupDeleteBlobSetup()
{
_StoreMock.Setup(m => m.GetBlobBeforeDate(It.IsAny<string>(),It.IsAny<DateTime>()))
.ReturnsAsync(new List<string> { "someUrl" });
}
private void SetupDeletionSuccessful(bool successfulDeletion)
{
_StoreMock.Setup(m => m.DeleteAttachmentAsync(It.IsAny<string>(), It.IsAny<string>())).ReturnsAsync(successfulDeletion);
}
Error is
Expected invocation on the mock exactly 1 times, but was 0 times:
m => m.GetBlobBeforeDate(It.IsAny<string>(), It.IsAny<DateTime>())
The error message is perfectly right. In your test the GetBlobBeforeDate() does not get called:
In your test setup the GetBlobBeforeDate method to return an empty list:
_StoreMock.Setup(m => m.GetBlobBeforeDate(It.IsAny<string>(),It.IsAny<DateTime>()))
.ReturnsAsync(new List<string>());
This means in you function there will be no blob url to delete. Because in your function you delete all blobUrls that have been returned by GetBlobBeforeDate. No items => nothing to delete.
If you want your test to verify that DeleteAttachmentAsync gets called exactly once you need a new setup. For example:
_StoreMock.Setup(m => m.GetBlobBeforeDate(It.IsAny<string>(),It.IsAny<DateTime>()))
.ReturnsAsync(new List<string> { "someUrl" });
Edit:
On more thing, this time in your code you call:
await _store.DeleteBlobAsync(Constants.AttachmentContainerName, blobName);
But what should be the corresponding Mock setup is:
_StoreMock.Setup(m => m.DeleteAttachmentAsync(It.IsAny<string>(), It.IsAny<string>())).ReturnsAsync(successfulDeletion);
As you can see you are comparing apples to oranges. The error is still right: DeleteAttachmentsAsync never gets called.
Also in your code you use both Constants.ContainerName nad Constants.AttachmentContainerName which also seems wrong.
I suggest to setup your Mocks a little bit more explicit and include the corresponding container name. For example:
_StoreMock.Setup(m => m.GetBlobBeforeDate(Constants.ContainerName, It.IsAny<DateTime>()))
.ReturnsAsync(new List<string> { "someUrl" });
This way the mock will also verify that the method has been called with the expected containerName parameter value.

Getting exception with Moq when trying to test Task

I'm trying to use Moq (4.10) on async calls but I cannot get the hang of it.
Search on how to do so and found answers which I've tried but I cannot make it to work .
This is my test
public class Test
{
[Fact]
public void Test_Create()
{
var repositoryMock = new Mock<IRepository>();
repositoryMock
.Setup(repo => repo.CreateAsync(It.IsAny<Aggregate >()))
.Returns(Task.CompletedTask);
/// also tried this
/// => .Returns(Task.FromResult(default(object)))
/// and
/// => .Returns(Task.FromResult(false)));
var useCase = new CreateUseCase(repositoryMock.Object);
Task.Run(async () => { await useCase.HandleAsync(new CreateRequest()); });
repositoryMock.VerifyAll();
}
}
resources
How can I tell Moq to return a Task?
Getting this exception
Moq.MockException: 'The following setups on mock
'Mock<.Repository.IRepository:00000001>' were not matched: IRepository
repo => repo.CreateAsync(It.IsAny < Aggregate>())
The repo looks like this
public interface IRepository
{
Task CreateAsync(Aggregate aggregate);
}
The UseCase
public class CreateUseCase : IUseCaseHandler<CreatRequest>
{
private IRepository _repository;
public CreateUseCase (IRepository repository)
{
_repository= repository?? throw new System.ArgumentNullException(nameof(repository));
}
public async Task HandleAsync(CreateRequest request, CancellationToken? cancellationToken = null)
{
Aggregate aggregate = new Aggregate();
aggregate.Create();
await _repository.CreateAsync(aggregate);
}
}
The Repository
public sealed class OrderRepository : ProxyRepository<OrderAggregate>, IOrderRepository
{
public OrderRepository(IUnitOfWork unitOfWork, INotificationDispatcher eventHandler)
: base(unitOfWork, eventHandler)
{
}
async Task IRepository.CreateAsync(Aggregate aggregate)
{
await base.AddAsync(aggregate);
}
}
What is it that I'm doing wrong or missing ?
We normally do not have to mock a method of an interface which doesn't return any unless I'm missing something in your question.
public class CreateUseCaseTests
{
[Fact]
public async Task HandleAsync_ShouldCreateRequest()
{
// Arrange
var repositoryMock = new Mock<IRepository>();
var sut = new CreateUseCase(repositoryMock.Object);
// Act
await sut.HandleAsync(new CreateRequest());
// Assert
repositoryMock.Verify(x => x.CreateAsync(It.IsAny<Aggregate>()), Times.Once);
}
}
I don't think your problem is with the Moq setup at all. The problem is that the unit test runs the meaningful code using Task.Run(), which spawns off a new thread. Then back on the original thread, you immediately test whether the Moq setup has been fulfilled. Since the code under test is launched on a different thread, there is a very real chance that the test for success comes before the code under test is executed.
You should change your unit test to run the test method using async & await, rather than spinning off a new thread. Note that the signature of the test case changes from void to async Task, and we await the code we're testing.
public class Test
{
[Fact]
public async Task Test_Create()
{
var repositoryMock = new Mock<IRepository>();
repositoryMock
.Setup(repo => repo.CreateAsync(It.IsAny<Aggregate >()))
.Returns(Task.CompletedTask);
var useCase = new CreateUseCase(repositoryMock.Object);
await useCase.HandleAsync(new CreateRequest());
repositoryMock.VerifyAll();
}
}

Delay in Moq return for async method not delaying

In my unit test, I'm trying to mock out the Run() async method from my interface IPipeline and simulate a delay, where it is called in the class PipelineScheduler
public interface IPipeline
{
Task Run();
}
Test Moq:
[SetUp]
public void SetUp()
{
_mockPipeline = new Mock<IPipeline>();
_mockPipeline.Setup(x => x.Run()).Returns(async () =>
{
await Task.Delay(3000);
});
_scheduler = new PipelineScheduler(_mockPipeline.Object);
}
However when I run the test and debug where the mocked method is called
await _pipeline.Run().ConfigureAwait(false);
there is no delay and execution continues after this line immediately.
But if I replace the mock with a stub class, the delay works fine.
private class MockPipeline : IPipeline
{
public async Task Run()
{
await Task.Delay(3000);
}
}
[SetUp]
public void SetUp()
{
_mockPipeline = new MockPipeline();
_scheduler = new PipelineScheduler(_mockPipeline);
}
So I suppose the question is what's different from how I'm creating the delay with moq vs my stubbed class?
The difference is that the setup is being configured incorrectly.
Returning a Task i.e:.Returns(Task.Delay(3000)); is all that is needed for the the setup to behave a desired. The previous setup is a fire and forget async void which is why the previous example did not wait and continued immediately.
The following minimal example demonstrates how the mock should have been setup
[TestClass]
public class MyTestClass {
[TestMethod]
public async Task MyTestMethod() {
//Arrange
var _mockPipeline = new Mock<IPipeline>();
_mockPipeline.Setup(x => x.Run()).Returns(Task.Delay(3000)).Verifiable();
var sut = new PipelineScheduler(_mockPipeline.Object);
//Act
await sut.MethodUnderTest();
//Assert
_mockPipeline.Verify();
}
}
public interface IPipeline {
Task Run();
}
public class PipelineScheduler {
private IPipeline _pipeline;
public PipelineScheduler(IPipeline pipeline) {
this._pipeline = pipeline;
}
public async Task MethodUnderTest() {
await _pipeline.Run().ConfigureAwait(false);
}
}
When exercised, the test delays for the configured 3 seconds.
An old question but I just had the problem and the correct way is not the accepted answer. The correct way is to use a function in the return.
i.e.
.Returns(() => Task.Delay(3000))
Without the () =>, the Delay is only applied once, even if you do multiple sequential calls, for example, like if you did this :
await _pipeline.Run().ConfigureAwait(false);
await _pipeline.Run().ConfigureAwait(false);
await _pipeline.Run().ConfigureAwait(false);
See this reproduction on dotnetfiddle.net

Fluent-ASsertions ShouldRaisePropertyChangeFor does not work for async Tasks?

I have a simple class that implements INotifyPropertyChanged, I invoke the property change on another thread, and I had a pretty hard time getting FluentAsserts to see that the propertyChanged was invoked. It does not seem to happen if I use a Task.Delay in an async Task method. But it does if I just sleep the thread.
The SimpleNotify class:
namespace FluentAssertPropertyThreads
{
class SimpleNotify : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void onChange(string name)
{
this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(name));
}
private int count = 0;
public int Count
{
get
{
return this.count;
}
set
{
if (this.count != value)
{
this.count = value;
this.onChange(nameof(this.Count));
}
}
}
}
}
and here are my unit tests:
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace FluentAssertPropertyThreads
{
[TestClass]
public class UnitTest1
{
private SimpleNotify simpleNotify;
private int notifyCount;
private void bumpCount()
{
this.simpleNotify.Count++;
}
private void SimpleNotify_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
SimpleNotify simpleNotify = sender as SimpleNotify;
if (simpleNotify == null)
{
throw new ArgumentNullException(nameof(sender), "sender should be " + nameof(SimpleNotify));
}
if (e.PropertyName.Equals(nameof(SimpleNotify.Count), StringComparison.InvariantCultureIgnoreCase))
{
this.notifyCount++;
}
}
[TestInitialize]
public void TestSetup()
{
this.notifyCount = 0;
this.simpleNotify = new SimpleNotify();
this.simpleNotify.PropertyChanged += SimpleNotify_PropertyChanged;
this.simpleNotify.MonitorEvents();
Thread thread = new Thread(this.bumpCount)
{
IsBackground = true,
Name = #"My Background Thread",
Priority = ThreadPriority.Normal
};
thread.Start();
}
[TestMethod]
public async Task TestMethod1()
{
await Task.Delay(100);
this.notifyCount.Should().Be(1); //this passes, so I know that my notification has be executed.
this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count); //but this fails, saying that I need to be monitoring the events (which I am above)
}
[TestMethod]
public void TestMethod2()
{
Thread.Sleep(100);
this.notifyCount.Should().Be(1); //this passes, so I know that my notification has be executed.
this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count); //this passes as I expected
}
}
}
The exact error is:
System.InvalidOperationException: Object is not being monitored for events or has already been garbage collected. Use the MonitorEvents() extension method to start monitoring events.
I don't see how MonitorEvents would care if I use await or Thread.Sleep. What am I missing? I get that await leaves the method and comes back in, whereas Thread.Sleep does not.
So when it leaves the TestMethod1 during the await, it is hitting a dispose on an object that FluentAsserts is using to track the properties? Could it? Should it?
Yes, the things is like you said: await pauses the execution of current method and create a state machine to get back to the method after the Delay is done. But the caller (a UnitTest engine) doesn't expect your tests to be asynchronous and simply ends the execution, which leads to the disposal of the objects.
Stephen Cleary wrote a brilliant MSDN article about Unit testing and async/await keywords, you probably should move your code out to the method returning the Task and wait for all of it in test, something like this:
async Task Testing()
{
await Task.Delay(100);
this.notifyCount.Should().Be(1);
this.simpleNotify.ShouldRaisePropertyChangeFor(x => x.Count);
}
[TestMethod]
public async Task TestMethod1()
{
await Testing();
}
but this still may fail as the logic after await may execute after the disposable being disposed.
Looks like version 5.0.0 will include support for Async tests with monitoring.
This Issue was raised and the work completed just waiting on the documentation to be updated and version 5.0.0 to be released.
But for the time being there is a prerelease with the code changes 5.0.0-beta2 can get it from the prerelease versions on NuGet
From the change log:
{Breaking} Replaced the old thread-unsafe MonitorEvents API with a new
Monitor extension method that will return a thread-safe monitoring
scope that exposes methods like Should().Raise() and metadata such as
OccurredEvents and MonitoredEvents
So the code with the updated NuGet will look like this:
[TestInitialize]
public void TestSetup()
{
this.notifyCount = 0;
this.simpleNotify = new SimpleNotify();
this.simpleNotify.PropertyChanged += SimpleNotify_PropertyChanged;
Thread thread = new Thread(this.bumpCount)
{
IsBackground = true,
Name = #"My Background Thread",
Priority = ThreadPriority.Normal
};
thread.Start();
}
[TestMethod]
public async Task TestMethod1()
{
using (var MonitoredSimpleNotify = this.simpleNotify.Monitor())
{
await Task.Delay(100);
this.notifyCount.Should().Be(1);
MonitoredSimpleNotify.Should().RaisePropertyChangeFor(x => x.Count); // New API for Monitoring
}
}

How to assert a task was restarted at least once

I am test driving a class that gets injected with a bunch of work tasks, runs the asynchronously and restarts them when completed until told to halt all tasks.
Since I am doing test first I needed to write a test that forces me to write the restart logic, and I have kind of successfully done this, but I don't think I did it very well.
Test code: (FakeTask is basically a test spy that keeps track on whether it was called and how many times)
[Fact]
public async void Start_GivenTask_RerunsTaskUntilStopped()
{
var agent = CreateKlarnaAgent();
var fakeTask = DoNothingTask();
agent.Start(fakeTask);
Thread.Sleep(500);
await agent.Stop();
Assert.True(fakeTask.TimesRun > 1);
}
(Relevant) production code:
public void Start(params IWorkTask[] workTasks)
{
_logWriter.Debug("Starting...");
_tasks = workTasks
.Select(workTask => workTask.DoWork().ContinueWith(task => OnTaskComplete(task, workTask)))
.ToArray();
}
private void OnTaskComplete(Task completedTask, IWorkTask workTask)
{
if (completedTask.IsFaulted)
{
foreach (var exception in completedTask.Exception.InnerExceptions)
{
_logWriter.Error("Unhandled exception thrown!", exception);
}
}
else workTask.DoWork().ContinueWith(task => OnTaskComplete(task, workTask));
}
public Task Stop()
{
return Task.WhenAll(_tasks)
.ContinueWith(t => { _logWriter.Debug("Stopped"); });
}
The test is now really depending on a race condition and it doesn't feel like a unit test at all. How can I get rid of the Thread.Sleep(500) call? Or is this simply something I should test in an integration test?
On a side note, I recommend against writing "task runners" in general, and also recommend against ContinueWith in particular since it is such a dangerous API.
In my opinion, the "repeat forever until canceled" logic is far more clearly expressed using a loop for "repeat" and a cancellation token for "canceled":
static async Task WorkAsync(Func<Task> doWork, CancellationToken token)
{
while (true)
{
await doWork();
token.ThrowIfCancellationRequested();
}
}
However, that said, if you want to unit test your "task runner" as-is, you'll need to make your FakeTask more intelligent. For example, you could have it set a signal when it reaches a given count and have your unit test wait on that:
class FakeTask : IWorkTask
{
private readonly TaskCompletionSource<object> _done = new TaskCompletionSource<object>();
public Task Done { get { return _done.Task; } }
public Task DoWork()
{
++TimesRun;
if (TimesRun > 1)
_done.TrySetResult(null);
return Task.CompletedTask;
}
}
[Fact]
public async Task Start_GivenTask_RerunsTaskUntilStopped()
{
var agent = CreateKlarnaAgent();
var fakeTask = DoNothingTask();
agent.Start(fakeTask);
await fakeTask.Done;
await agent.Stop();
Assert.True(fakeTask.TimesRun > 1); // spurious test at this point
}

Categories