I have a unit test which uses the OWIN TestServer class to host my Web Api ApiController classes for testing.
I first wrote the unit test when the REST API did not have the HTTPS (SSL) requirement baked into the Controller itself.
My unit test looked something like this:
[TestMethod]
[TestCategory("Unit")]
public async Task Test_MyMethod()
{
using (var server = TestServer.Create<TestStartup>())
{
//Arrange
var jsonBody = new JsonMyRequestObject();
var request = server.CreateRequest("/api/v1/MyMethod")
.And(x => x.Method = HttpMethod.Post)
.And(x => x.Content = new StringContent(JsonConvert.SerializeObject(jsonBody), Encoding.UTF8, "application/json"));
//Act
var response = await request.PostAsync();
var jsonResponse =
JsonConvert.DeserializeObject<JsonMyResponseObject>(await response.Content.ReadAsStringAsync());
//Assert
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Now that I've applied the attribute to enforce HTTPS, my unit test fails.
How do I fix my test so that, all things being equal, the test passes again?
To fix this unit test, you need to change the base address for the TestServer.
Once the server has been created set the BaseAddress property on the created object to use an "https" address. Remember the default BaseAddress value is http://localhost.
In which case, you can use https://localhost.
The changed unit test would look as follows:
[TestMethod]
[TestCategory("Unit")]
public async Task Test_MyMethod()
{
using (var server = TestServer.Create<TestStartup>())
{
//Arrange
server.BaseAddress = new Uri("https://localhost");
var jsonBody = new JsonMyRequestObject();
var request = server.CreateRequest("/api/v1/MyMethod")
.And(x => x.Method = HttpMethod.Post)
.And(x => x.Content = new StringContent(JsonConvert.SerializeObject(jsonBody), Encoding.UTF8, "application/json"));
//Act
var response = await request.PostAsync();
var jsonResponse =
JsonConvert.DeserializeObject<JsonMyResponseObject>(await response.Content.ReadAsStringAsync());
//Assert
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Related
I saw the very same issue in this post but none of the solutions works now in ASP.Net Core 2.2. When I debug the unit test the Response property is still null and thus the test fails.
I have been reading the asp.net core docs for an answer how to mock the ControllerContext so that the Response property has a value but I couldn't find anything working.
Here is the line inside the action that makes troubles:
Response.Headers.Add("Access-Control-Expose-Headers", "Content-Range");
So what I have ended up with in the unit test set up is:
var routeData = new RouteData();
routeData.Values.Add("controller", "Home");
var headerDictionary = new HeaderDictionary();
var response = new Mock<HttpResponse>();
response.SetupGet(r => r.Headers).Returns(headerDictionary);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(a => a.Response).Returns(response.Object);
var actionContext = new ActionContext(
httpContext.Object,
routeData,
new ControllerActionDescriptor());
_controller.ControllerContext = new ControllerContext(actionContext);
A lot of the setup can be avoided by using the DefaultHttpContext which would have the needed properties already populated. This includes the Response and its members
//..
var routeData = new RouteData();
routeData.Values.Add("controller", "Home");
var httpContext = DefaultHttpContext(); //<--
var actionContext = new ActionContext(
httpContext,
routeData,
new ControllerActionDescriptor());
_controller.ControllerContext = new ControllerContext(actionContext);
//...
After exercising the subject under test, the response can the obtained from the context used by the controller and desired behavior asserted.
//...
//Assert
var response = httpContext.Response;
var key = "Access-Control-Expose-Headers";
Assert.True(response.Headers.TryGetValues(key, out var value));
Assert.Equals("Content-Range", value.FirstOrDefault()
Finally I have managed to mock the Controller's HttpContext with the following code:
protected void SetUpControllerContext(ClaimsPrincipal principal)
{
var headerDictionary = new HeaderDictionary();
var response = new Mock<HttpResponse>();
response.SetupGet(r => r.Headers).Returns(headerDictionary);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(a => a.Response).Returns(response.Object);
httpContext.SetupGet(a => a.User).Returns(principal);
this.SutController.ControllerContext = new ControllerContext()
{
HttpContext = httpContext.Object
};
}
Now altering the Response property inside Controller's actions is allowed and doesn't trigger an error.
I am writing a unit test cases fro Azure function.In my function app configuration section i have added my connection string.
When the azure function runs in azure below line of code is working fine.
var connStr = ConfigurationManager.ConnectionStrings["SqlConnection"].ConnectionString;
If i am calling the azure function from unit test case this line is throwing exception.
"object reference not set to an instance of an object".
Form unit test case how i can execute this line of code.(using Moq).
Test Method :
[TestMethod]
public async Task GetDataFromAzureSQL_Test()
{
var req = new HttpRequestMessage();
var obj = new Modeltest
{
id = 1,
name ="",
location=""
};
var content = new StringContent(JsonConvert.SerializeObject(obj).ToString(), Encoding.UTF8, "application/json");
req.Content = content;
var result = await Function.Function1.Run(req, new Mock<ILogger>().Object);
Assert.IsTrue("OK" == result.StatusCode.ToString());
}
I have two Asp.Net Core systems, which are completely independent, meaning that they reside on different web domains. Still, they are in the same solution in Visual Studio. Both Asp.Net Core systems would be hosted, for example, on these two domains:
https://client-localhost:8080 and https://api-localhost:8081
where the client app makes calls to many different routes of the Api domain in order to get data.
I have no problems doing integration tests (using NUnit) for the Api system, for example:
// Integration Test for the Api
[TestFixture]
class IntegrationTestShould
{
public TestServer GetTestServerInstance()
{
return new TestServer(new WebHostBuilder()
.UseStartup<TestServerStartup>()
.UseEnvironment("TestInMemoryDb"));
}
[Test]
public async Task ReturnProductDataFromTestInMemoryDb()
{
using (var server = GetTestServerInstance())
{
var client = server.CreateClient();
var response = await client.GetAsync("/products"); // equivalent to: https://api-localhost:8081/products
var responseString = await response.Content.ReadAsStringAsync();
Assert.AreEqual("{ Shows product data coming from the Api }", responseString);
}
}
}
In order to do a proper integration test for the client app, I would like to make Api calls from the client app to the Api.
Is it possible to create one single test method in which I can start both test servers (client and Api) and consume the api through my client?
I can imagine, for example, to inject the Api test server into the client test server so that I can consume the Api through my client app.
Does something like the following exist?
// Integration test for the client that relies on the Api
[TestFixture]
class IntegrationTestShould
{
public TestServer GetApiTestServerInstance()
{
return new TestServer(new WebHostBuilder()
.UseStartup<ApiTestServerStartup>()
.UseEnvironment("TestInMemoryDb"));
}
public TestServer GetClientTestServerInstance()
{
return new TestServer(new WebHostBuilder()
.UseStartup<ClientTestServerStartup>()
.UseEnvironment("Development"));
}
[Test]
public async Task ShowProductsFromApiAtClientLevel()
{
using (var apiServer = GetApiTestServerInstance())
using (var clientServer = GetClientTestServerInstance())
{
var client = clientServer.CreateClient(apiServer);
var response = await client.GetAsync("/products"); // equivalent to: https://client-localhost:8080/products which relies on https://api-localhost:8081/products
var responseString = await response.Content.ReadAsStringAsync();
Assert.AreEqual("{ Shows product data coming from the api at client level }",
responseString);
}
}
}
Thanks to mjwills simple, but powerful question, I simply tested the code as it was and there were compile errors. But this got the stone rolling and I tried and failed until I figured it out.
What I basically did is inject the instantiated HttpClient of the ApiServer into my client app backend where an HttpRequest is made to the ApiServer. So whenever the client app wants to make an api call to the api server, it does so by using the injected HttpClient of the ApiServer.
Here is a snapshot of my Integration test and a little bit of my client app code that should anyone guide into the right direction:
// My abbreviated and redacted integration test using NUnit
[TestFixture]
public class IntegrationTestShould
{
public TestServer GetApiTestServerInstance()
{
return new TestServer(new WebHostBuilder()
.UseStartup<ApiTestServerStartup>()
.UseEnvironment("TestInMemoryDb"));
}
public TestServer GetClientTestServerInstance(TestServer apiTestServer)
{
// In order to get views rendered:
// 1. ContentRoot folder must be set when TestServer is built (or views are not found)
// 2. .csproj file of test project must be adjusted, see http://www.dotnetcurry.com/aspnet-core/1420/integration-testing-aspnet-core (or references for view rendering are missing)
var apiHttpClient = apiTestServer.CreateClient();
apiHttpClient.BaseAddress = new Uri(#"https://api-localhost:8081");
var currentDirectory =
Path.GetDirectoryName(Path.GetDirectoryName(TestContext.CurrentContext.TestDirectory));
var contentRoot = Path.GetFullPath(Path.Combine(currentDirectory, #"..\..\ProjectThatContainsViews"));
return new TestServer(new WebHostBuilder()
.UseStartup<ClientTestServerStartup>()
.UseContentRoot(contentRoot)
// register instantiated apiHttpClient in client app
.ConfigureServices(collection => collection.AddSingleton(apiHttpClient))
.UseEnvironment("ClientTestServer"));
}
[Test]
public async Task CorrectlyReturnProductsViewResult()
{
using (var apiServer = GetApiTestServerInstance())
using (var clientServer = GetClientTestServerInstance(apiServer))
{
var clientHttpClient = clientServer.CreateClient();
var response = await clientHttpClient.GetAsync("/products");
var responseString = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
Assert.AreEqual("text/html; charset=utf-8",
response.Content.Headers.ContentType.ToString());
}
}
}
// My heavily abbreviated and redacted client app backend
public class HttpRequestBuilder
{
private readonly HttpClient _httpClient;
public HttpRequestBuilder(IServiceProvider serviceProvider)
{
// get instantiated apiHttpClient from client app dependency container (when running in test environment)
// or create new one (the case when running in environment other than test)
_httpClient = serviceProvider.GetService(typeof(HttpClient)) as HttpClient ?? new HttpClient();
}
public async Task<HttpResponseMessage> SendAsync()
{
// Setup request
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(#"https://api-localhost:8081/products")
};
// Send request
var result = await _httpClient.SendAsync(request);
// should have returned product data from api
var content = await result.Content.ReadAsStringAsync();
return result; // api product data processed further at client app level
}
}
I have the following Method:
public async Task<SecurityRoleDeleteResult> DeleteSecurityRoleByRoleId(int securityRoleId)
{
string url = $"{_housingDataSecurityConfiguration.HousingDataSecurityWebApiUrl}SecurityRoles/Delete";
HttpResponseMessage message = _apiClientService.Post(url, securityRoleId);
if (message.StatusCode == HttpStatusCode.InternalServerError)
{
return SecurityRoleDeleteResult.ErrorOccurred;
}
int intResult = 0;
var apiResult = await message.Content.ReadAsStringAsync();
if (int.TryParse(apiResult, out intResult))
{
return (SecurityRoleDeleteResult)intResult;
}
else
{
return SecurityRoleDeleteResult.ErrorOccurred;
}
}
I'm now trying to write a unit test for it and so far have:
[Test]
public async Task DeleteSecurityRoleByRoleId()
{
_mockApiClientService.Setup(a => a.Post(It.IsAny<string>(), It.IsAny<int>()))
.Returns(new HttpResponseMessage {StatusCode = HttpStatusCode.OK});
SecurityRoleDeleteResult result = await _securityRoleService.DeleteSecurityRoleByRoleId(It.IsAny<int>());
Assert.AreEqual(SecurityRoleDeleteResult.Success, result);
}
The issue here is that when running the test in the _securityRoleService.DeleteSecurityRoleByRoleId method at the point I try to set var apiResult message.content is null, because in this instance I'm only mocking so crashes.
How can I mock this out so that my test will work?
I figured out my issue. Rather than delete my question, I thought I'd post my change to the test. Basically I hadn't mocked the content.
HttpContent content = new StringContent("4");
_mockApiClientService.Setup(a => a.Post(It.IsAny<string>(), It.IsAny<int>()))
.Returns(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = content });
So depending on the content type you may want to have returned you may need to change the type of content.
i am writing unit test cases. I am trying to write unit test for this method but showing error. How to unit test this method in mvc3 framework and rhino mock.
public ActionResult UnderConstruction()
{
Response.StatusCode = (int)HttpStatusCode.TemporaryRedirect;
ErrorModel model = new ErrorModel()
{
ErrorMessage = "This page is still under construction; please check back later.",
Title = "Under Construction"
};
return View("Error", model);
}
It's the Response that is null, not Response.StatusCode. You need to mock HttpContextBase and HttpResponseBase, and then create and assign the controller's ControllerContext.
The test will look something like this (sorry if I fudge the Rhino Mock code; I use Moq normally):
// arrange
var httpContext = MockRepository.GenerateMock<HttpContextBase>();
var request = MockRepository.GenerateMock<HttpRequestBase>();
var response = MockRepository.GenerateMock<HttpResponseBase>();
// stub both Request and Response, for good measure.
httpContext.Stub(x => x.Request).Return(request);
httpContext.Stub(x => x.Response).Return(response);
var controller = new YourController();
// create and assign the controller context
var context = new ControllerContext(httpContext,
new RouteData(),
controller);
controller.ControllerContext = context;
// act
var actual = controller.UnderConstruction() as ViewResultBase;
// assert
Assert.That(actual, Is.Not.Null);
Assert.That(controller.Response.StatusCode, Is.EqualTo(HttpStatusCode.TemporaryRedirect));
// etc.