I have an interface IDiscosClient, for testing/demo purposes while I'm developing the app, I want a mock to return a new model when the .GetSingle<T>() method is called with a random delay of between 1 and 5 seconds. This is mostly so I can see that all of my various loading spinner components and whatnot work.
So, I thought I'd be able to do something like this:
Fixture fixture = new();
fixture.Customize(new DiscosModelFixtureCustomizationNoLinks());
builder.Services.AddTransient(_ =>
{
IDiscosClient client = Substitute.For<IDiscosClient>();
DiscosObject obj = fixture.Create<DiscosObject>();
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
return client;
});
However, while there seems to be a delay when I first call the method, once this has resolved, it just seems to return the completed task with the same model in it every time I call it for that IDiscosClient instance.
Is there a simple enough way to accomplish this?
So the issue is that the code above only creates a fresh Task the first time and then returns the same one (which has already completed) each subsequent time.
To fix this, we can either change the code above to:
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(_ => Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
Or, for legibilities sake, we can extract it into a method and make the whole code block:
builder.Services.AddTransient(_ =>
{
IDiscosClient client = Substitute.For<IDiscosClient>();
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(GetDiscosObject);
return client;
});
async Task<DiscosObject> GetDiscosObject(CallInfo _)
{
await Task.Delay(Random.Shared.Next(1000, 5000));
return fixture.Create<DiscosObject>();
}
Related
I want to test the Save() method which is residing inside the Contributor class. This method, in turn, opens a dialog which, when finished loading, actions an event that triggers the method that I want to mock - PushToPortal.
internal class Contributor
{
private readonly IEntryPointWrapper _entryPoint;
private readonly ISyncDialog _dialog;
public event Action OnPushToWeldCompleted;
internal void Save(Document document)
{
...
void Ptp() => PushToPortal(information);
_dialog.ShowDialog(Ptp);
}
}
When ShowDialog is called, OnLoadingCompleted += Ptp; is invoked.
The problem begins just here. PushToPortal looks like this:
internal virtual void PushToPortal(Information information)
{
var pushToPortal = Task.Run(() => {
var result = _entryPoint.ProcessElements(information);
});
pushToPortal.ContinueWith(task => OnPushToPortalCompleted?.Invoke(), TaskScheduler.FromCurrentSynchronizationContext());
}
Basically, when running the tests, they continue running while the async method is still processing and when asserting, the callback does not retrieve the needed information unless I use Thread.Sleep, which is not a solution that I'd like.
The working solution would be:
_entryPointMock.Setup(epm => epm.ProcessElements(It.IsAny<Information>()))
.Callback<WeldInformation>(information => actualWeldInformation = information)
.Returns(new InfoResult { Status = Status.Succeed, InfoCount = 1 });
Thread.Sleep(5000);
_contributor.Save(document);
I tried mocking the PushToPortal method and use a Callback to retrieve its arguments, without going into ProcessElements, but it does not seem to do anything at all.
_entryPointMock.Setup(epm => epm.ProcessElements(It.IsAny<Information>()))
.Returns(new InfoResult { Status = Status.Succeed, InfoCount = 1 });
var mock = new Mock<Contributor>();
mock.CallBase = true;
mock.Setup(x => x.PushToPortal(It.IsAny<Information>()))
.Callback<Information>(information => actualInformation = information);
_contributor.Save(document);
Therefore, how can I mock PushToPortal properly such that I retrieve the information, without actually entering the new thread which processes the elements? I am looking for a solution which does not imply changing the current code very much - an example which I thought of (and would not like to implement) would've been breaking down the functionality and returning a Task.
I have this unit test:
[Fact(DisplayName = "Http test")]
public async Task Http_Test_Should_Return_Something()
{
var model = new Test();
model.test1= "tt";
var messageHandler = new Mock<HttpMessageHandler>();
messageHandler.ClientBuilder(url, delegate () { return JsonConvert.SerializeObject(model); });
var httpClient = new HttpClient(messageHandler.Object, false);
var func = new Class(httpClient, _propertiesSetOnContruct);
//RUN FIRST TIME TO SET THE PRIVATE FIELDS AND DO THE NORMAL PROPERTY GATHERING
await func.RunLogic();
//RUN THE SECOND TIME TO GET THE DICT VALUE SET AND DONE AND RUN THE REST OF THE LOGIC
await func.RunLogic();
}
This is my ClientBuilder Code in which I setup the handler to run the function:
public static void TokenClientBuilder(this Mock<HttpMessageHandler> handler, string requestUrl, Func<string> function)
{
handler.Protected()
.As<IHttpMessageHandlerMock>()
.Setup(x => x.SendAsync(
It.Is<HttpRequestMessage>(r =>
r.Method == HttpMethod.Post &&
r.RequestUri == new Uri(requestUrl)),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new HttpResponseMessage()
{
Content = new StringContent(function(),
Encoding.UTF8,
"application/json")
});
}
So, to resume the code that I have there in the unit test, I basically need to cover all the code of the class that I am trying to check, the problem is that I need to run it 2 times within the same unit test, since the Dictionary that I want to populate can only be populated when I run the first time and the second check will only happen when the user goes again to the class.
Since the Httpclient is disposed when I go for the 2nd round I lose my content and I end up with a null value and when I try to do a Json Convert it obviously causes an exception.
I even thought of mocking the dictionary within the class, but it is private and I can't access it.
My question here is, is there any way, shape or form to mock the MessageHandler in order for the Httpclient to persist in the 2nd round of the unit test or do I need to do something else?
The code that I am trying to test is not relevant for here because it won't change and the constructor props and the props of the class are private, so nothing to do there.
In my code I have a method such as:
void PerformWork(List<Item> items)
{
HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
foreach (var item in items)
{
await itemHandler.PerformIndividualWork(item);
}
});
}
Where Item is just a known model and itemHandler just does some work based off of the model (the ItemHandler class is defined in a separately maintained code base as nuget pkg I'd rather not modify).
The purpose of this code is to have work done for a list of items in the background but synchronously.
As part of the work, I would like to create a unit test to verify that when this method is called, the items are handled synchronously. I'm pretty sure the issue can be simplified down to this:
await MyTask(1);
await MyTask(2);
Assert.IsTrue(/* MyTask with arg 1 was completed before MyTask with arg 2 */);
The first part of this code I can easily unit test is that the sequence is maintained. For example, using NSubstitute I can check method call order on the library code:
Received.InOrder(() =>
{
itemHandler.PerformIndividualWork(Arg.Is<Item>(arg => arg.Name == "First item"));
itemHandler.PerformIndividualWork(Arg.Is<Item>(arg => arg.Name == "Second item"));
itemHandler.PerformIndividualWork(Arg.Is<Item>(arg => arg.Name == "Third item"));
});
But I'm not quite sure how to ensure that they aren't run in parallel. I've had several ideas which seem bad like mocking the library to have an artificial delay when PerformIndividualWork is called and then either checking a time elapsed on the whole background task being queued or checking the timestamps of the itemHandler received calls for a minimum time between the calls. For instance, if I have PerformIndividualWork mocked to delay 500 milliseconds and I'm expecting three items, then I could check elapsed time:
stopwatch.Start();
// I have an interface instead of directly calling HostingEnvironment, so I can access the task being queued here
backgroundTask.Invoke(...);
stopwatch.Stop();
Assert.IsTrue(stopwatch.ElapsedMilliseconds > 1500);
But that doesn't feels right and could lead to false positives. Perhaps the solution lies in modifying the code itself; however, I can't think of a way of meaningfully changing it to make this sort of unit test (testing tasks are run in order) possible. We'll definitely have system/integration testing to ensure the issue caused by asynchronous performance of the individual items doesn't happen, but I would like to hit testing here at this level as well.
Not sure if this is a good idea, but one approach could be to use an itemHandler that will detect when items are handled in parallel. Here is a quick and dirty example:
public class AssertSynchronousItemHandler : IItemHandler
{
private volatile int concurrentWork = 0;
public List<Item> Items = new List<Item>();
public Task PerformIndividualWork(Item item) =>
Task.Run(() => {
var result = Interlocked.Increment(ref concurrentWork);
if (result != 1) {
throw new Exception($"Expected 1 work item running at a time, but got {result}");
}
Items.Add(item);
var after = Interlocked.Decrement(ref concurrentWork);
if (after != 0) {
throw new Exception($"Expected 0 work items running once this item finished, but got {after}");
}
});
}
There are probably big problems with this, but the basic idea is to check how many items are already being handled when we enter the method, then decrement the counter and check there are still no other items being handled. With threading stuff I think it is very hard to make guarantees about things from tests alone, but with enough items processed this can give us a little confidence that it is working as expected:
[Fact]
public void Sample() {
var handler = new AssertSynchronousItemHandler();
var subject = new Subject(handler);
var input = Enumerable.Range(0, 100).Select(x => new Item(x.ToString())).ToList();
subject.PerformWork(input);
// With the code from the question we don't have a way of detecting
// when `PerformWork` finishes. If we can't change this we need to make
// sure we wait "long enough". Yes this is yuck. :)
Thread.Sleep(1000);
Assert.Equal(input, handler.Items);
}
If I modify PerformWork to do things in parallel I get the test failing:
public void PerformWork2(List<Item> items) {
Task.WhenAll(
items.Select(item => itemHandler.PerformIndividualWork(item))
).Wait(2000);
}
// ---- System.Exception : Expected 1 work item running at a time, but got 4
That said, if it is very important to run synchronously and it is not apparent from glancing at the implementation with async/await then maybe it is worth using a more obviously synchronous design, like a queue serviced by only one thread, so that you're guaranteed synchronous execution by design and people won't inadvertently change it to async during refactoring (i.e. it is deliberately synchronous and documented that way).
I'm trying to unit test a bit of code to make sure that a callback is invoked, but it seems that even without an "Assert"-call in the method it will pass. Consider the code example below:
public void Show_ShowSomeAdFormat_CallbackShouldBeInvoked()
{
AdManager adManager = new AdManager();
adManager.Show<VideoTestAdFormat>((response) =>
{
//Assert.Pass(); <--- With or without this, the test will pass.
//I need it to only pass if it reaches this. How is it done?
});
}
If you read the comments I think you will understand what I am after.
Thank you!
Use a captured bool.
public void Show_ShowSomeAdFormat_CallbackShouldBeInvoked()
{
AdManager adManager = new AdManager();
bool callbackInvoked = false;
adManager.Show<VideoTestAdFormat>((response) => callbackInvoked = true);
// If the callback is invoked asynchronously,
// you'll need a way to wait here for Show to complete.
Assert.IsTrue(callbackInvoked);
}
EDIT:
If you're using .NET 4, you might have Show return a Task that completes when Show is done doing work. In earlier .NET versions, you can return a ManualResetEvent. "Return" can be a return value, or an overload of Show with an out parameter.
I am testing a method for a service that makes a Web API call. Using a normal HttpClient works fine for unit tests if I also run the web service (located in another project in the solution) locally.
However when I check in my changes the build server won't have access to the web service so the tests will fail.
I've devised a way around this for my unit tests by creating an IHttpClient interface and implementing a version that I use in my application. For unit tests, I make a mocked version complete with a mocked asynchronous post method. Here's where I have run into problems. I want to return an OK HttpStatusResult for this particular test. For another similar test I will be returning a bad result.
The test will run but will never complete. It hangs at the await. I am new to asynchronous programming, delegates, and Moq itself and I've been searching SO and google for a while learning new things but I still can't seem to get past this problem.
Here is the method I am trying to test:
public async Task<bool> QueueNotificationAsync(IHttpClient client, Email email)
{
// do stuff
try
{
// The test hangs here, never returning
HttpResponseMessage response = await client.PostAsync(uri, content);
// more logic here
}
// more stuff
}
Here's my unit test method:
[TestMethod]
public async Task QueueNotificationAsync_Completes_With_ValidEmail()
{
Email email = new Email()
{
FromAddress = "bob#example.com",
ToAddress = "bill#example.com",
CCAddress = "brian#example.com",
BCCAddress = "ben#example.com",
Subject = "Hello",
Body = "Hello World."
};
var mockClient = new Mock<IHttpClient>();
mockClient.Setup(c => c.PostAsync(
It.IsAny<Uri>(),
It.IsAny<HttpContent>()
)).Returns(() => new Task<HttpResponseMessage>(() => new HttpResponseMessage(System.Net.HttpStatusCode.OK)));
bool result = await _notificationRequestService.QueueNotificationAsync(mockClient.Object, email);
Assert.IsTrue(result, "Queue failed.");
}
What am I doing wrong?
Thank you for your help.
You're creating a task but never starting it, so it's never completing. However, don't just start the task - instead, change to using Task.FromResult<TResult> which will give you a task which has already completed:
...
.Returns(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));
Note that you won't be testing the actual asynchrony this way - if you want to do that, you need to do a bit more work to create a Task<T> that you can control in a more fine-grained manner... but that's something for another day.
You might also want to consider using a fake for IHttpClient rather than mocking everything - it really depends on how often you need it.
Recommend #Stuart Grassie's answer above.
var moqCredentialMananger = new Mock<ICredentialManager>();
moqCredentialMananger
.Setup(x => x.GetCredentialsAsync(It.IsAny<string>()))
.ReturnsAsync(new Credentials() { .. .. .. });
With Mock.Of<...>(...) for async method you can use Task.FromResult(...):
var client = Mock.Of<IHttpClient>(c =>
c.PostAsync(It.IsAny<Uri>(), It.IsAny<HttpContent>()) == Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))
);
Try using ReturnsAsync.
In asynchronous methods it works, I believe the basis to solve your problem should be similar.
_mocker.GetMock<IMyRepository>()
.Setup(x => x.GetAll())
.ReturnsAsync(_myFakeListRepository.GetAll());