I have an Azure function that stores applications settings in Azure Blob Storage. To unit test the class that gets and adds the settings, I use moq to have the blob storage abstraction class (blobStorageRepository) throw an exception. It mostly works. however, I have two tests that fail.
I Have other unit tests that mocks the _blobStorageRepository. All work just fine including the test against the "Get" method correctly throws the exception but the "Add" exception tests fail. I've included the actual test below
Fact(DisplayName = "AddUserSettingsAsync - InvalidOperationException")]
[Trait("Category", "Unit Test")]
public async Task SettingsStoreAddUserSettingsTestWithException()
{
string userObject = Guid.NewGuid().ToString();
string correlationId = Guid.NewGuid().ToString();
string body = File.ReadAllText("TestData/userSettings.json");
UserSettingsObject userSettingsObject = JsonConvert.DeserializeObject<UserSettingsObject>(body);
var iFunctionEnvironment = TestHelpers.GetEnvironmentVariable("Test");
Uri.TryCreate("http://localhost", UriKind.Absolute, out Uri uri);
var iblobStorageRepositoryMoq = new Mock<IBlobStorageRepository>();
iblobStorageRepositoryMoq
.Setup(mock => mock.Add(logger, correlationId, body, userObject))
.ThrowsAsync(new Exception("Function Add threw an exception"));
var iblobStorageRepository = iblobStorageRepositoryMoq.Object;
SettingsStore settingsStore = new SettingsStore(iFunctionEnvironment, iblobStorageRepository);
Exception exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await settingsStore.AddUserSettingsAsync(logger, correlationId, userSettingsObject, userObject));
Assert.Equal("Function Add threw an exception", exception.Message);
Assert.Null(exception.InnerException);
}
Here's the interface for the blogStoreRepository:
Task<bool> Add(ILogger logger, string correlationId, string settingsObject, string settingsObjectName);
Any help would be greatly appreciated!
If an invoked mock does not behave as expected, most times it is because the setup did not match what was actually invoked.
Consider loosening the expectation using It.IsAny<T>()
Fact(DisplayName = "AddUserSettingsAsync - InvalidOperationException")]
[Trait("Category", "Unit Test")]
public async Task SettingsStoreAddUserSettingsTestWithException() {
//Arrange
string userObject = Guid.NewGuid().ToString();
string correlationId = Guid.NewGuid().ToString();
string body = File.ReadAllText("TestData/userSettings.json");
UserSettingsObject userSettingsObject = JsonConvert.DeserializeObject<UserSettingsObject>(body);
var iFunctionEnvironment = TestHelpers.GetEnvironmentVariable("Test");
Uri.TryCreate("http://localhost", UriKind.Absolute, out Uri uri);
var iblobStorageRepositoryMoq = new Mock<IBlobStorageRepository>();
iblobStorageRepositoryMoq
.Setup(mock => mock.Add(It.IsAny<ILogger>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.ThrowsAsync(new InvalidOperationException("Function Add threw an exception"));
//The SUT
var subjectUnderTest = new SettingsStore(iFunctionEnvironment, iblobStorageRepositoryMoq.Object);
//Act
InvalidOperationException exception = await Assert.ThrowsAsync<InvalidOperationException>(() => subjectUnderTest.AddUserSettingsAsync(logger, correlationId, userSettingsObject, userObject));
//Assert
Assert.Equal("Function Add threw an exception", exception.Message);
Assert.Null(exception.InnerException);
}
Note the change to the setup and also if asserting that InvalidOperationException was thrown then mock should actually throw an InvalidOperationException
//...
var iblobStorageRepositoryMoq = new Mock<IBlobStorageRepository>();
iblobStorageRepositoryMoq
.Setup(mock => mock.Add(It.IsAny<ILogger>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.ThrowsAsync(new InvalidOperationException("Function Add threw an exception"));
//...
Related
We are trying the Test Driven Development for our .Net core 3.1 Web API and using XUnit for the same.
In my controller, I have test cases or [Fact] written for the status codes 200,404 and other scenarios.
When it comes to unit testing 500 internal server error - I am not sure how to pass the code, or how to assert it.
It always fails for me by using the below code.
How to successfully test the 500 - Internal server error in my unit test?
I am very new to TDD any comment is helpful, below is the code :
[Fact] // 404 Sample unit test method
public async Task Get_OnNotFound_Returns404()
{
// Arrange
var mockService = new Mock<IService>();
mockService.Setup(service => service.GetTypes(It.IsAny<string>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new List<TypeResponse>());
var sut = new HomeController(mockService.Object);
//Act
var result = await sut.GetSampleMethod("foo") as ObjectResult;
//Assert
Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode);
}
// Below is my always failing 500 code unit test
[Fact]
public async Task Returns_500_When_Returns_Error()
{
// Arrange
var mockService = new Mock<IService>();
mockService.Setup(service => service.GetTypes(It.IsAny<string>(),
It.IsAny<CancellationToken>())).ReturnsAsync(new List<TypeResponse>());
var sut = new HomeController(mockService.Object);
//Act
var result = await GetSampleMethod("foo") as ObjectResult;
//Assert
Assert.Equal(StatusCodes.Status500InternalServerError, result.StatusCode);
}
Below is my controller method :
[HttpGet]
public async Task<IActionResult> GetSampleMethod(string id,
CancellationToken cancellationToken)
{
try
{
var result = await _service.GetSampleMethod(id, cancellationToken);
if (result.Any())
{
return Ok(result);
}
return StatusCode(StatusCodes.Status404NotFound,
new Error { Message = "No Records ." });
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError,
new Error
{
Message = !string.IsNullOrWhiteSpace(ex.Message)
? ex.Message : "An unhandled error occurred."
});
}
}
It's clear that your method returns Status500InternalServerError in case of an exception. So you have to mock an exception for it:
mockService.Setup(service => service.GetTypes(It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.Throws(new Exception());
I would like to unit test the next method:
public static async Task SetResponseBody(HttpResponse response, string message)
{
var originalResponseBody = response.Body;
var responseBody = new MemoryStream();
response.Body = responseBody;
response.ContentType = "application/json";
dynamic body = new { Message = message };
string json = JsonSerializer.Serialize(body);
await response.WriteAsync(json);
response.Body.Seek(0, SeekOrigin.Begin);
await responseBody.CopyToAsync(originalResponseBody);
}
The last two lines are used from this post.
The current unit test implementation is:
[TestMethod]
public async Task SetResponseBody_TestMessageAsync()
{
var expected = "TestMessage";
string actual = null;
var responseMock = new Mock<HttpResponse>();
responseMock
.Setup(_ => _.Body.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
.Callback((byte[] data, int offset, int length, CancellationToken token) =>
{
if (length > 0)
actual = Encoding.UTF8.GetString(data);
})
.Returns(Task.CompletedTask);
await ResponseRewriter.SetResponseBody(responseMock.Object, expected);
}
The unit tests fails due to a NullReferenceException which is raised once the test hits the 'await response.WriteAsync(json);' line of code. Could you point me in the right direction in order to fix this exception, so the test will pass?
Summarized: The unit tests needs to check if the given 'TestMessage' is actually written to the Body of the response.
Background information:
I'm calling the SetResponseBody method in order to modify the response body as soon as the 'OnRedirectToIdentityProvider' event is raised from AddOpenIdConnect.
OnRedirectToIdentityProvider = async e =>
{
// e is of type RedirectContext
if (e.Request.Path.StartsWithSegments("/api")))
{
if (e.Response.StatusCode == (int)HttpStatusCode.OK)
{
e.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
// TestMessage is a const
// e.Response is readonly (get) so it's not possible to set it directly.
await ResponseRewriter.SetResponseBody(e.Response, TestMessage);
}
e.HandleResponse();
}
await Task.CompletedTask;
}
.NET Core 3.1, WebApi, OpenId
There are too many internals that need to be configured for the abstract HttpResponse to work as intended when mocking it.
I would suggest using DefaultHttpContext and extracting the default response created within that context.
[TestMethod]
public async Task SetResponseBody_TestMessageAsync() {
//Arrange
string expected = "TestMessage";
string actual = null;
HttpContext httpContext = new DefaultHttpContext();
HttpResponse response = httpContext.Response
//Act
await ResponseRewriter.SetResponseBody(response, expected);
//Assert
//...
}
for the assertion, extract the content of the response body and assert its expected behavior.
We can't write unit test for all method especially system library.
so solution is make virtual function and use below method instead of direct await response.WriteAsync(json);
public virtual async Task WriteAsync(string json)
{
await response.WriteAsync(json);
}
and then
yourClassObject_Where_SetResponseBody_Exist.Setup(m => m.WriteAsync
(It.IsAny<string>()));
I've a function app. I wrote a unit test project(xunit) to test my function app code. In unit test method I'm calling Run method of my function app. When request is 'Get' my function app returns a simple json object. While this is working fine in my machine, in all my colleague's machines following line is throwing StackOverFlowException. When I checked the exception details, StackTrace is null.
request.CreateResponse(HttpStatusCode.OK, jObject)
In debug window I see the error as "An unhandled exception occurred in System.private.corlib.dll"
Function App:
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, ILogger log)
{
if (req.Method.Method.Equals(HttpMethod.Get.Method))
{
NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(req.RequestUri.Query);
operation = queryString.Get("operation");
if (operation == "GetVersion")
{
version = VersionHistory.GetVersion("ABC");// returns
// {"ABC" : "2.1"}
return req.CreateResponse(HttpStatusCode.OK, version);
//Above line causes StackOverFlowException
}
else
{
return 'Error message'
}
}
}
VersionHistory in above code is just a static class I'm using. It simply returns a json object something like {{"ABC": "0.1.2"}}. It has nothing to do with .net framework version.
public static JObject GetVersion(string key)
{
// some logic. This logic is hit only once. I'm sure this is not
// causing any exception
return JObject.Parse("{\"" + key + "\":\"0.1.2\"}");
}
unit test:
public async Task Run_WhenGetRequest_ShouldReturnAppropriateFunctionAppVersion()
{
const string QUERYSTRING_KEY = "operation";
const string QUERYSTRING_VALUE = "GetVersion";
const string FUNCTION_APP_NAME = "ABC";
const string FUNCTION_APP_VERSION = "2.1";
_request.Method = HttpMethod.Get;
NameValueCollection queryList = HttpUtility.ParseQueryString(_request.RequestUri.Query);
if (queryList.Get(QUERYSTRING_KEY) == null)
{
string queryString = QueryHelpers.AddQueryString(_request.RequestUri.ToString(), QUERYSTRING_KEY, QUERYSTRING_VALUE);
_request.RequestUri = new System.Uri(queryString);
}
HttpResponseMessage response = await FunctionAppClassName.Run(_request, _logger);
// Assert code
}
I've constructed request object in a Fixture class as following and mocked Logger instance.
Request = new HttpRequestMessage
{
Content = new StringContent("", Encoding.UTF8, "application/json"),
RequestUri = new Uri("http://localhost/")
};
var services = new ServiceCollection().AddMvc().AddWebApiConventions().Services.BuildServiceProvider();
Request.Properties.Add(nameof(HttpContext), new DefaultHttpContext { RequestServices = services });
Any idea how to fix this?
Finally we found the issue. Problem was as we are calling Http triggered function app from test case, it was not able to find the MediaType. After adding media type in response like below it worked.
return req.CreateResponse(HttpStatusCode.OK, jObject, "application/json");
I'm still not sure why this issue occurred only in Visual Studio 2019 16.6.x and 16.7.x but not in 16.4.x. We would appreciate if someone can throw some light on this
Thank you those who gave your time.
I am trying to mock a service, and receiving the following error below in unit test. How can this be resolved? What does this error mean? Is it something maybe with await/async?
services.AddSingleton(a =>
{
var mock = new Mock<IUserResolverService>();
mock.Setup(b => b.GetUser()).Returns(5);
return mock.Object;
});
System.NotSupportedException : Can not apply commission concerns to component Common.Services.IUserResolverService_175a16cd-d798-4fbd-b343-1ba0eb5e0ad6 because it appears to be a target-less proxy. Currently those are not supported. DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy) IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy) ScopedWindsorServiceProvider.GetServiceInternal(Type serviceType, Boolean isOptional) line 55
More Code Data:
private async Task<IServiceProvider> GetProvider()
{
var services = new ServiceCollection();
services.AddSingleton(new Mock<ISharedServicesApiClient>().Object);
services.AddSingleton(new Mock<IAddressValidator>().Object);
services.AddSingleton(new Mock<IAddressValidationService>().Object);
services.RegisterMappingProfiles(
new PropertyManagementDataMappingProfile(),
new ApplicationServicesMappingProfile()
);
services.AddSingleton(a =>
{
var mock = new Mock<IUserResolverService>();
mock.Setup(b => b.GetUser()).Returns(5);
return mock.Object;
});
services.AddDbContext<PropertyContext>(
a => a.UseInMemoryDatabase("TestDb").UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll),
ServiceLifetime.Singleton);
services.AddDbContext<AuditablePropertyContext>(
a => a.UseInMemoryDatabase("TestDb").UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll),
ServiceLifetime.Singleton);
services.AddSingleton<DbContext, AuditablePropertyContext>();
services.AddSingleton<DbContext, PropertyContext>();
services.AddSingleton<IAddressAppService, AddressAppService>();
services.AddSingleton<IAddressRepository, AddressRepository>();
services.AddSingleton<IPartyContactRepository, PartyContactRepository>();
services.AddSingleton<IPartyMailingAddressRepository, PartyMailingAddressRepository>();
var provider = services.GetServiceProvider();
var db = provider.GetRequiredService<PropertyContext>();
await db.AddTestData();
return provider;
}
[Fact]
public async Task AddNewAddressAlternateAddTest()
{
var provider = await GetProvider();
var manager = provider.GetRequiredService<IAddressAppService>();
var request =
SitusAndPartyAddressDataSeed.GetSingleApnRequestForAdd(AddressType.Alternate);
var result = await manager.UpdatePartyAndSitusAddresses(request);
// new AddressId is 3
Assert.True(result.Body == NewAddressId);
Following up on the "hunch" with some reasoning, you're setting up a singleton for a mocked instance of IUserResolverService, but as written you're uniquely composing and returning that mocked object using a delegate rather than just creating a single instance of the completed mock and registering it instead like you are with all the other ones. Given the blend of synchronous and asynchronous operations, it looks like your test is causing Moq to struggle with the lifecycle/creation of the object.
If we change this:
services.AddSingleton(a =>
{
var mock = new Mock<IUserResolverService>();
mock.Setup(b => b.GetUser()).Returns(5);
return mock.Object;
});
To this:
var mock = new Mock<IResolverService();
mock.Setup(b => b.GetUser()).Returns(5);
services.AddSingleton(mock.Object);
You're then passing in the actual single instance of the mock instead, which might resolve whatever is causing the issue, be it the lifecycle or late binding (or something else, but hey).
I'm trying to unit test a method and assert that the result is of a specific type when calling a mocked 'request provider' which handles HttpClient logic, though I have setup the mocked interface it always returns null.
Previously when using HttpClient I've mocked the HttpMessageHandler and handled the business logic in the method at the other side, however the third party API we are using requires multiple calls to their rest api using GET requests, so I wanted a solution that kept the solution more 'DRY'
The following is the setup I am currently trying to use
_requestProvider.Setup(
svc => svc.GetAsync<PlayerBalance>(It.IsAny<string>(), It.IsAny<string>()))
.Returns(() => Task.FromResult(new PlayerBalance
{
Balance = 0
}));
_playerService = new PlayerService(_playerRepository.Object,
_secretsService.Object,
_awsConfig,
_requestProvider.Object);
My act/assertion
var result = await _playerService.GetPlayerBalanceAsync(request);
result.Should().BeOfType<PlayerBalance>();
The method under test
public async Task<PlayerBalance> GetPlayerBalanceAsync(PlayerBalanceRequest playerBalanceRequest)
{
if (string.IsNullOrEmpty(playerBalanceRequest.Login)) throw new Exception("Login is a required parameter.");
string url = $#"myrestendpoint";
var result = await _requestProvider.GetAsync<List<PlayerBalance>>(url);
return result.FirstOrDefault();
}
And where it's failing invocation on the mock
public async Task<TResult> GetAsync<TResult>(string uri, string token = "")
{
HttpClient httpClient = CreateHttpClient(token);
HttpResponseMessage response = await httpClient.GetAsync(uri);
await HandleResponse(response);
string serialized = await response.Content.ReadAsStringAsync();
TResult result = await Task.Run(() =>
JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));
return result;
}
When running in 'Strict' it is telling me that it expected invocation but no setup was found. I'm not really sure what else needs setting up here.
the result returned from the last slice of code is always null, but I want it to be PlayerBalance { Balance = 0 }
Any help would be appreciated.
Just to clarify I have also tried my setup in the following ways
.Returns(Task.FromResult(new PlayerBalance
{
Balance = 0
}));
.ReturnsAsync(new PlayerBalance {
Balance = 0
});
You are mocking:
_requestProvider.GetAsync<PlayerBalance>(url)
When you should be mocking:
_requestProvider.GetAsync<List<PlayerBalance>>(url)
Setup the mock to expect the desired behavior:
_requestProvider.Setup(
svc => svc.GetAsync<List<PlayerBalance>>(It.IsAny<string>(), It.IsAny<string>())
)
.ReturnsAsync(() => new List<PlayerBalance>() { new PlayerBalance
{
Balance = 0
}});