Using Moq to mock an asynchronous method for a unit test
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.
Setting up a C# Test with Moq against Async methods
You need to fix the set up on the async calls
Moq has a ReturnsAsync
that would allow the mocked async method calls to flow to completion.
client
.Setup(_ => _.ReplaceDocumentAsync(It.IsAny<Uri>(), It.IsAny<object>(), It.IsAny<RequestOptions>()))
.ReturnsAsync(new ResourceResponse<Document>());
You typically want to avoid newing up Task
s manually
Mock Async method on Service using Moq
ArtistsControllerTests.Setup()
is not being invoked so the mocks are not being setup before the test is exercised.
Therefore when the test is exercised they will return null.
Your setup code is correct, it just is not getting called.
either change that Setup
method to a constructor
public ArtistsControllerTests() {
_mockArtistsService.Reset();
_mockPermissionsService
.Setup(service => service.GetPermissionsAsync(It.IsAny<HttpContext>()))
.Returns(Task.FromResult(new Permissions { UserId = "112233", IsAdministrator = false }));
_mockArtistsService.Setup(service => service.GetAllArtists(It.IsAny<string>(), false)).Returns(new ArtistCardDtoCollection());
}
or adorn the method with [TestInitilize]
attribute
[TestInitialize]
public void Setup() {
_mockArtistsService.Reset();
_mockPermissionsService
.Setup(service => service.GetPermissionsAsync(It.IsAny<HttpContext>()))
.Returns(Task.FromResult(new Permissions { UserId = "112233", IsAdministrator = false }));
_mockArtistsService.Setup(service => service.GetAllArtists(It.IsAny<string>(), false)).Returns(new ArtistCardDtoCollection());
}
or just move the arrange into the test itself
[TestMethod]
public async Task GetArtists_ReturnsOKStatusCode() {
// arrange
_mockArtistsService.Reset();
_mockPermissionsService
.Setup(service => service.GetPermissionsAsync(It.IsAny<HttpContext>()))
.Returns(Task.FromResult(new Permissions { UserId = "112233", IsAdministrator = false }));
_mockArtistsService.Setup(service => service.GetAllArtists(It.IsAny<string>(), false)).Returns(new ArtistCardDtoCollection());
var artistsController = new ArtistsController(_mockPermissionsService.Object, _mockArtistsService.Object, _mockLogger.Object);
// act
var getArtistsResult = await artistsController.GetArtists();
var okResult = getArtistsResult as OkObjectResult;
// assert
Assert.IsInstanceOfType(okResult, typeof(OkObjectResult));
}
Mocking a method that uses an asynchronous callback with moq
First observation was that you pass in a mocked ISocket
in state
and try to cast it to Socket
in async callback which will result in a null error which means connectDone.Set()
is never called so WaitOne
will not unblock.
Change that to
private void ConnectCallback(IAsyncResult result) {
ISocket client = (ISocket)result.AsyncState;
client.EndConnect(result);
connectDone.Set();
}
Second observation was that you were not setting up the mocked calls correctly. No need for reflection here as you needed to get the passed arguments from the mock and invoke then in the mock callback setup
The following is based on your original code. Review it to get an understanding of what was explained above.
[TestClass]
public class SocketManagerTests {
[TestMethod]
public void ConnectTest() {
//Arrange
var mockSocket = new Mock<ISocket>();
//async result needed for callback
IAsyncResult mockedIAsyncResult = Mock.Of<IAsyncResult>();
//set mock
mockSocket.Setup(_ => _.BeginConnect(
It.IsAny<EndPoint>(), It.IsAny<AsyncCallback>(), It.IsAny<object>())
)
.Returns(mockedIAsyncResult)
.Callback((EndPoint ep, AsyncCallback cb, object state) => {
var m = Mock.Get(mockedIAsyncResult);
//setup state object on mocked async result
m.Setup(_ => _.AsyncState).Returns(state);
//invoke provided async callback delegate
cb(mockedIAsyncResult);
});
var manager = new SocketManager(mockSocket.Object);
//Act
var actual = manager.Connect();
//Assert
Assert.IsTrue(actual);
mockSocket.Verify(_ => _.EndConnect(mockedIAsyncResult), Times.Once);
}
}
Finally I believe you should consider changing this code to use TPL to get around the whole call back and IAsyncResult
drama. Basically exposing an async API and wrapping the calls with a TaskCompletionSource<T>
but I guess that is outside of the scope of this question.
Moq Framework to unit test a method that returns a task
Mock returns false, because when you call HttpPost
parameters don't match with ones that were set up. The second parameter is different.
You can set up mock like that:
mock
.Setup(x => x.HttpPost("http://test.com", It.IsAny<StringContent>(), "token"))
.Returns(Task.FromResult(true)); //or .ReturnsAsync(true);
It tells mocking framework, that second parameter can be any object of type StringContent
.
Docs can be found here: https://github.com/Moq/moq4/wiki/Quickstart#matching-arguments
Unit testing method with async
Are you trying to mock the HttpClient
? If so this isn't the recommended approach; instead of mocking HttpClient
instantiate a real HttpClient
, passing a mocked HttpMessageHandler
into the constructor.
Source: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/
Edit: to read the contents of a HTTP response you need to call the response.Content.ReadAsStringAsync()
method instead of .ToString()
.
How do you mock an IAsyncEnumerable?
If you don’t want to do anything special, e.g. a delayed return which is usually the point of async enumerables, then you can just create a generator function that returns the values for you.
public static async IAsyncEnumerable<string> GetTestValues()
{
yield return "foo";
yield return "bar";
await Task.CompletedTask; // to make the compiler warning go away
}
With that, you can simply create a mock for your service and test your object:
var serviceMock = new Mock<IMyService>();
serviceMock.Setup(s => s.CallSomethingReturningAsyncStream()).Returns(GetTestValues);
var thing = new Thing(serviceMock.Object);
var result = await thing.MyMethodIWantToTest();
Assert.Equal("foo", result[0]);
Assert.Equal("bar", result[1]);
Of course, since you are now using a generator function, you can also make this more complicated and add actual delays, or even include some mechanism to control the yielding.
How can I throw Exception for async function using Moq
Considering the asynchronous nature of the code under test, it would be better if the test code be asynchronous as well. Moq is async capable
[Fact]
public async Task CreateCSVFile_Failure() {
//Arrange
var dtData = new DataTable();
string fileName = "";
var mockClient = new Mock<IHttpHandler>();
this._iADLS_Operations = new ADLS_Operations(mockClient.Object);
mockClient
.Setup(repo => repo.PostAsync(It.IsAny<string>(), It.IsAny<HttpContent>(), It.IsAny<string>()))
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.BadRequest));
mockClient
.Setup(repo => repo.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<string>()))
.ThrowsAsync(new Exception("Some message here"));
//Act
Func<Task> act = () => this._iADLS_Operations.CreateCSVFile(dtData, fileName);
//Assert
Exception ex = await Assert.ThrowsAsync<Exception>(act);
Assert.Contains("Exception occurred while executing method:", ex.Message);
}
Note the use of Moq's ReturnsAsync
and ThrowsAsync
in the setup, along with xUnit's Assert.ThrowsAsync
This now allows you to avoid making blocking calls like .Result
which could potentially lead to deadlocks.
Unit Testing Async Controller Method Returning Null with NUnit and Moq
The UserRequest
instance you're passing into Authenticate
is a different instance than what you're setting up your mock to expect. So, since the inputs don't match, the mock is not executed, and the default mock behavior takes over.
Either remove the mock conditional (i.e., use It.IsAny<UserRequest>()
), or pass the same instance to your mock and your controller method.
Related Topics
Creating Threads - Task.Factory.Startnew VS New Thread()
How to Fill Part of Image with Color
Copying Free Hand Drawing from Panel in Visual Studio 2013
How to Create "Embedded" SQL 2008 Database File If It Doesn't Exist
How to Programmatically Limit My Program's CPU Usage to Below 70%
How Better to Resolve Dependencies in Object Created by Factory
The Source Was Not Found, But Some or All Event Logs Could Not Be Searched
How to Store and Retrieve Credentials on Windows Using C#
Can a Tcp C# Client Receive and Send Continuously/Consecutively Without Sleep
Error Message "Cs5001 Program Does Not Contain a Static 'Main' Method Suitable for an Entry Point"
Comboboxes Are Linked for Some Reason
Mvvm Show New Window from Vm When Seperated Projects
Delete a Single Record from Entity Framework
Setting Up Swagger (ASP.NET Core) Using the Authorization Headers (Bearer)
Httpclient Single Instance with Different Authentication Headers