I have a MVC controller that loads a resource file and uses Server.MapPath to get the path to the file. I want to mock out the Server property in the controller object using Microsoft Fakes framework (I know how to do this using other frameworks).
Here's the code:
[HttpGet]
public ActionResult GeneratePdf(string reportId)
{
var template = LoadTemplate(reportId);
var document = pdfWriter.Write(GetReportModel(reportId), template);
return File(document, MediaTypeNames.Application.Pdf);
}
private byte[] LoadTemplate(string reportId)
{
var templatePath = Server.MapPath(string.Format("~/ReportTemplates/{0}.docx", reportId));
using(var templateContent = System.IO.File.OpenText(templatePath))
{
return Encoding.Default.GetBytes(templateContent.ReadToEnd());
}
}
The part I'm trying to mock out is the "Server.MapPath" method.
As of Visual Studio 2012 Update 1, you can detour the Controller.Server property using Stubs.
With the following .Fakes file in your test project:
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/" Diagnostic="true">
<Assembly Name="System.Web" Version="4.0.0.0"/>
<StubGeneration>
<Clear/>
<Add FullName="System.Web.HttpContextBase!"/>
<Add FullName="System.Web.HttpServerUtilityBase!"/>
</StubGeneration>
<ShimGeneration>
<Clear/>
</ShimGeneration>
</Fakes>
You can write the a unit test like this:
[TestMethod]
public void TestMethod1()
{
var target = new TestController();
var serverStub = new StubHttpServerUtilityBase();
serverStub.MapPathString = (path) => path.Replace("~", string.Empty).Replace("/", #"\");
var contextStub = new StubHttpContextBase();
contextStub.ServerGet = () => serverStub;
target.ControllerContext = new ControllerContext();
target.ControllerContext.HttpContext = contextStub;
var result = (FilePathResult) target.Index();
Assert.AreEqual(#"\Content\Test.txt", result.FileName);
}
With the upcoming Update 2, you will also be able to detour Controller.Server property directly using Shims. Here is the additional .Fakes file you will need with this approach.
<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/" Diagnostic="true">
<Assembly Name="System.Web.Mvc" Version="4.0.0.0"/>
<StubGeneration>
<Clear/>
</StubGeneration>
<ShimGeneration>
<Clear/>
<Add FullName="System.Web.Mvc.Controller!"/>
</ShimGeneration>
</Fakes>
And here is the test:
[TestMethod]
public void TestMethod2()
{
using (ShimsContext.Create())
{
var target = new TestController();
var serverStub = new StubHttpServerUtilityBase();
serverStub.MapPathString = (path) => path.Replace("~", string.Empty).Replace("/", #"\");
var controllerShim = new ShimController(target);
controllerShim.ServerGet = () => serverStub;
var result = (FilePathResult)target.Index();
Assert.AreEqual(#"\Content\Test.txt", result.FileName);
}
}
Please note that this approach does not work in the current version (Update 1), due to limitations in Fakes runtime related to assemblies, like System.Web.Mvc that allow partially trusted callers. If you try to run the second test today, you will get a VerificationException.
Related
I am trying to adapt the Microsoft example for AF 3.0/.NET Core 3.1/xUnit (see Strategies for testing your code in Azure Functions) to work with AF 3.0/.NET 5.0/xUnit. However, I am running into compilation issues.
The Azure Function is a simple HTTP Trigger (GET only), ExportFuncApp.csproj file is as follows:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.12" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.2.0" OutputItemType="Analyzer" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.5.2" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
The ExportFunc.cs file is as follows:
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
namespace ExportFuncApp
{
public class ExportFunc
{
[Function(nameof(ExportFunc))]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("ExportFunc");
logger.LogInformation("C# HTTP trigger function processed a request.");
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
response.WriteString("Welcome to Azure Functions!");
return response;
}
}
}
Nothing special there. However, XUnit tests provided by Microsoft (.NET Core 3.1) are not really applicable to .NET 5.0. There was a StackOverflow article on the subject: Testing an Azure Function in .NET 5. 4 solutions were given in the article and all of them have compile issues. The first solution given was (ExportFuncUnitTests2.cs):
using Xunit;
using ExportFuncApp;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using Moq;
using Microsoft.Azure.Functions.Worker;
using System.Net;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Functions.Worker.Http;
namespace ExportFuncAppUnitTestsXunit
{
public class ExportFuncUnitTests2
{
[Fact]
public async Task Http_trigger_should_return_known_string()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddScoped<ILoggerFactory, LoggerFactory>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var context = new Mock<FunctionContext>();
context.SetupProperty(c => c.InstanceServices, serviceProvider);
var byteArray = Encoding.ASCII.GetBytes("test");
var bodyStream = new MemoryStream(byteArray);
var request = new Mock<HttpRequestData>(context.Object);
request.Setup(r => r.Body).Returns(bodyStream);
request.Setup(r => r.CreateResponse()).Returns(() =>
{
var response = new Mock<HttpResponseData>(context.Object);
response.SetupProperty(r => r.Headers, new HttpHeadersCollection());
response.SetupProperty(r => r.StatusCode);
response.SetupProperty(r => r.Body, new MemoryStream());
return response.Object;
});
var result = await ExportFunc.Run(request.Object, context.Object);
result.HttpResponse.Body.Seek(0, SeekOrigin.Begin);
// Assert
var reader = new StreamReader(result.HttpResponse.Body);
var responseBody = await reader.ReadToEndAsync();
Assert.NotNull(result);
Assert.Equal(HttpStatusCode.OK, result.HttpResponse.StatusCode);
Assert.Equal("Hello test", responseBody);
}
}
}
This results in a compilation error in ExportFuncUnitTests2.cs:
CS1061 'HttpResponseData' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'HttpResponseData' could be found (are you missing a using directive or an assembly reference?)
for:
var result = await ExportFunc.Run(request.Object, context.Object);
The second solution given in the article involves FakeHttpRequestData.cs:
using System;
using System.Collections.Generic;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker;
using System.IO;
using System.Security.Claims;
namespace ExportFuncAppUnitTestsXunit
{
class FakeHttpRequestData : HttpRequestData
{
public FakeHttpRequestData(FunctionContext functionContext, Uri url, Stream body = null) : base(functionContext)
{
Url = url;
Body = body ?? new MemoryStream();
}
public override Stream Body { get; } = new MemoryStream();
public override HttpHeadersCollection Headers { get; } = new HttpHeadersCollection();
public override IReadOnlyCollection<IHttpCookie> Cookies { get; }
public override Uri Url { get; }
public override IEnumerable<ClaimsIdentity> Identities { get; }
public override string Method { get; }
public override HttpResponseData CreateResponse()
{
return new FakeHttpResponseData(FunctionContext);
}
}
}
and, FakeHttpResponseData.cs:
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Azure.Functions.Worker;
using System.Net;
using System.IO;
namespace ExportFuncAppUnitTestsXunit
{
class FakeHttpResponseData : HttpResponseData
{
public FakeHttpResponseData(FunctionContext functionContext) : base(functionContext)
{
}
public override HttpStatusCode StatusCode { get; set; }
public override HttpHeadersCollection Headers { get; set; } = new HttpHeadersCollection();
public override Stream Body { get; set; } = new MemoryStream();
public override HttpCookies Cookies { get; }
}
}
And the test is (ExportFuncUnitTests2.cs):
using Xunit;
using ExportFuncApp;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using Moq;
using Microsoft.Azure.Functions.Worker;
using System;
using System.Net;
using Microsoft.Extensions.Logging.Abstractions;
namespace ExportFuncAppUnitTestsXunit
{
public class ExportFuncUnitTests2
{
[Fact]
public async Task Http_trigger_should_return_known_string()
{
// Arrange
var body = new MemoryStream(Encoding.ASCII.GetBytes("{ \"test\": true }"));
var context = new Mock<FunctionContext>();
var request = new FakeHttpRequestData(
context.Object,
new Uri("https://stackoverflow.com"),
body);
// Act
var function = new ExportFunc(new NullLogger<ExportFunc>());
var result = await function.Run(request, context);
result.HttpResponse.Body.Position = 0;
// Assert
var reader = new StreamReader(result.HttpResponse.Body);
var responseBody = await reader.ReadToEndAsync();
Assert.NotNull(result);
Assert.Equal(HttpStatusCode.OK, result.HttpResponse.StatusCode);
Assert.Equal("Hello test", responseBody);
}
}
}
The ExportFuncUnitTests2.cs has the following compilation errors:
CS1729 'ExportFunc' does not contain a constructor that takes 1 arguments
for:
var function = new ExportFunc(new NullLogger<ExportFunc>());
and
CS1503 Argument 2: cannot convert from 'Moq.Mock<Microsoft.Azure.Functions.Worker.FunctionContext>' to 'Microsoft.Azure.Functions.Worker.FunctionContext'
for:
var result = await function.Run(request, context);
I the article Guide for running C# Azure Functions in an isolated process was somewhat useful but no help in terms of unit testing. Maybe I am missing the point. Since there is no examples/documentation on how to do unit testing for AF 3.0 with .NET 5.0 from Microsoft then I should not be trying to do that?
Any pointers will be greatly appreciated. Thanks!
Your Run() function in ExportFunc is not awaitable. Therefore, you should replace the line:
var result = await function.Run(request, context)
with:
var result = function.Run(request, context).
If you want to test an awaitable function (i.e., async Run()), then you'd call it from your Unit Test with await.
I have an ASP.NET MVC Core application that I am writing unit tests for. One of the action methods uses User name for some functionality:
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
which obviously fails in the unit test. I looked around and all suggestions are from .NET 4.5 to mock HttpContext. I am sure there is a better way to do that. I tried to inject IPrincipal, but it threw an error; and I even tried this (out of desperation, I suppose):
public IActionResult Index(IPrincipal principal = null) {
IPrincipal user = principal ?? User;
SettingsViewModel svm = _context.MySettings(user.Identity.Name);
return View(svm);
}
but this threw an error as well.
Couldn't find anything in the docs either...
The controller’s User is accessed through the HttpContext of the controller. The latter is stored within the ControllerContext.
The easiest way to set the user is by assigning a different HttpContext with a constructed user. We can use DefaultHttpContext for this purpose, that way we don’t have to mock everything. Then we just use that HttpContext within a controller context and pass that to the controller instance:
var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, "example name"),
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim("custom-claim", "example claim value"),
}, "mock"));
var controller = new SomeController(dependencies…);
controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = user }
};
When creating your own ClaimsIdentity, make sure to pass an explicit authenticationType to the constructor. This makes sure that IsAuthenticated will work correctly (in case you use that in your code to determine whether a user is authenticated).
In previous versions you could have set User directly on the controller, which made for some very easy unit tests.
If you look at the source code for ControllerBase you will notice that the User is extracted from HttpContext.
/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
/// </summary>
public ClaimsPrincipal User => HttpContext?.User;
and the controller accesses the HttpContext via ControllerContext
/// <summary>
/// Gets the <see cref="Http.HttpContext"/> for the executing action.
/// </summary>
public HttpContext HttpContext => ControllerContext.HttpContext;
You will notice that these two are read only properties. The good news is that ControllerContext property allows for setting it's value so that will be your way in.
So the target is to get at that object. In Core HttpContext is abstract so it is a lot easier to mock.
Assuming a controller like
public class MyController : Controller {
IMyContext _context;
public MyController(IMyContext context) {
_context = context;
}
public IActionResult Index() {
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
return View(svm);
}
//...other code removed for brevity
}
Using Moq, a test could look like this
public void Given_User_Index_Should_Return_ViewResult_With_Model() {
//Arrange
var username = "FakeUserName";
var identity = new GenericIdentity(username, "");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
var model = new SettingsViewModel() {
//...other code removed for brevity
};
var mockContext = new Mock<IMyContext>();
mockContext.Setup(m => m.MySettings(username)).Returns(model);
var controller = new MyController(mockContext.Object) {
ControllerContext = new ControllerContext {
HttpContext = mockHttpContext.Object
}
};
//Act
var viewResult = controller.Index() as ViewResult;
//Assert
Assert.IsNotNull(viewResult);
Assert.IsNotNull(viewResult.Model);
Assert.AreEqual(model, viewResult.Model);
}
There is also the possibility to use the existing classes, and mock only when needed.
var user = new Mock<ClaimsPrincipal>();
_controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = user.Object
}
};
In my case, I needed to make use of Request.HttpContext.User.Identity.IsAuthenticated, Request.HttpContext.User.Identity.Name and some business logic sitting outside of the controller. I was able to use a combination of Nkosi's, Calin's and Poke's answer for this:
var identity = new Mock<IIdentity>();
identity.SetupGet(i => i.IsAuthenticated).Returns(true);
identity.SetupGet(i => i.Name).Returns("FakeUserName");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity.Object);
var mockAuthHandler = new Mock<ICustomAuthorizationHandler>();
mockAuthHandler.Setup(x => x.CustomAuth(It.IsAny<ClaimsPrincipal>(), ...)).Returns(true).Verifiable();
var controller = new MyController(...);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext()
{
User = mockPrincipal.Object
};
var result = controller.Get() as OkObjectResult;
//Assert results
mockAuthHandler.Verify();
I want to hit my Controllers directly and just use DI like AutoFac. To do this I first registering ContextController.
var identity = new GenericIdentity("Test User");
var httpContext = new DefaultHttpContext()
{
User = new GenericPrincipal(identity, null)
};
var context = new ControllerContext { HttpContext = httpContext};
builder.RegisterInstance(context);
Next I enable property injection when I register the Controllers.
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Controller")).PropertiesAutowired();
Then User.Identity.Name is populated, and I do not need to do anything special when calling a method on my Controller.
public async Task<ActionResult<IEnumerable<Employee>>> Get()
{
var requestedBy = User.Identity?.Name;
..................
I would look to implement an Abstract Factory Pattern.
Create an interface for a factory specifically for providing user names.
Then provide concrete classes, one which provides User.Identity.Name, and one that provides some other hard coded value that works for your tests.
You can then use the appropriate concrete class depending on production versus test code. Perhaps looking to pass the factory in as a parameter, or switching to the correct factory based on some configuration value.
interface IUserNameFactory
{
string BuildUserName();
}
class ProductionFactory : IUserNameFactory
{
public BuildUserName() { return User.Identity.Name; }
}
class MockFactory : IUserNameFactory
{
public BuildUserName() { return "James"; }
}
IUserNameFactory factory;
if(inProductionMode)
{
factory = new ProductionFactory();
}
else
{
factory = new MockFactory();
}
SettingsViewModel svm = _context.MySettings(factory.BuildUserName());
I got a brownfield .net 4.8 project that I needed to convert to .net 5.0 and I wanted to keep as much of the original code as possible, including the unit-/integration tests. The test for Controllers relied on the Context a lot so I created this Extension method to enable setting tokens, claims and headers:
public static void AddContextMock(
this ControllerBase controller,
IEnumerable<(string key, string value)> claims = null,
IEnumerable<(string key, string value)> tokens = null,
IEnumerable<(string key, string value)> headers = null)
{
HttpContext mockContext = new DefaultHttpContext();
if(claims != null)
{
mockContext.User = SetupClaims(claims);
}
if(tokens != null)
{
mockContext.RequestServices = SetupTokens(tokens);
}
if(headers != null)
{
SetupHeaders(mockContext, headers);
}
controller.ControllerContext = new ControllerContext()
{
HttpContext = mockContext
};
}
private static void SetupHeaders(HttpContext mockContext, IEnumerable<(string key, string value)> headers)
{
foreach(var header in headers)
{
mockContext.Request.Headers.Add(header.key, header.value);
}
}
private static ClaimsPrincipal SetupClaims(IEnumerable<(string key, string value)> claimValues)
{
var claims = claimValues.Select(c => new Claim(c.key, c.value));
return new ClaimsPrincipal(new ClaimsIdentity(claims, "mock"));
}
private static IServiceProvider SetupTokens(IEnumerable<(string key, string value)> tokenValues)
{
var mockServiceProvider = new Mock<IServiceProvider>();
var authenticationServiceMock = new Mock<IAuthenticationService>();
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), null));
var tokens = tokenValues.Select(t => new AuthenticationToken { Name = t.key, Value = t.value });
authResult.Properties.StoreTokens(tokens);
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(It.IsAny<HttpContext>(), null))
.ReturnsAsync(authResult);
mockServiceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authenticationServiceMock.Object);
return mockServiceProvider.Object;
}
This uses Moq but can be adapted to other mocking frameworks. The authentication type is hardcoded to "mock" since I rely on default authentication but this could be supplied as well.
It is used as such:
_controllerUnderTest.AddContextMock(
claims: new[]
{
(ClaimTypes.Name, "UserName"),
(ClaimTypes.MobilePhone, "1234"),
},
tokens: new[]
{
("access_token", "accessTokenValue")
},
headers: new[]
{
("header", "headerValue")
});
If you're using Razor pages and want to override the claims:
[SetUp]
public void Setup()
{
var user = new ClaimsPrincipal(new ClaimsIdentity(
new Claim[] {
new("dateofbirth", "2000-10-10"),
new("surname", "Smith") },
"mock"));
_razorModel = new RazorModel()
{
PageContext = new PageContext
{
HttpContext = new DefaultHttpContext() { User = user }
}
};
}
I am developing an ASP.NET web API application. I am unit testing to every component in my application. I am using Moq Unit Test framework to mock the data. Now I am trying to mock the Configuration.Formatters.JsonFormatter in my Unit test because my action under unit test is using it as follows:
public HttpResponseMessage Register(model)
{
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest,
Content = new ObjectContent<List<string>>(errors, Configuration.Formatters.JsonFormatter)
};
}
I am trying to mock the Configuration.Formatters.JsonFormatter in the Unit Test as follow.
[TestMethod]
public void Register_ReturnErrorsWithBadRequest_IfValidationFails()
{
PostUserRegistration model = new PostUserRegistration {
Name = "Wai Yan Hein",
Email = "waiyanhein#gmail.com",
Password = ""
};
Mock<JsonMediaTypeFormatter> formatterMock = new Mock<JsonMediaTypeFormatter>();
Mock<MediaTypeFormatterCollection> formatterCollection = new Mock<MediaTypeFormatterCollection>();
formatterCollection.Setup(x => x.JsonFormatter).Returns(formatterMock.Object);
Mock<HttpConfiguration> httpConfigMock = new Mock<HttpConfiguration>();
httpConfigMock.Setup(x => x.Formatters).Returns(formatterCollection.Object);
Mock<IAccountRepo> accRepoMock = new Mock<IAccountRepo>();
AccountsController controller = new AccountsController(accRepoMock.Object);
controller.Configuration = httpConfigMock.Object;
controller.ModelState.AddModelError("", "Faking some model error");
HttpResponseMessage response = controller.Register(model);
Assert.AreEqual(response.StatusCode, System.Net.HttpStatusCode.BadRequest);
}
When I run my unit tests, it is giving me this error.
System.NotSupportedException: Invalid setup on a non-virtual
(overridable in VB) member: x => x.JsonFormatter
So, how can I fix that error and how can I mock Configuration.Formatters.JsonFormatter?
You should not have to test that the framework is doing what it was designed to do. Instead, similar to what was suggested before in comments, I suggest using the built in methods of the ApiController and also refactoring the action to the syntax suggested from documentation for that version of Asp.Net Web API
public IHttpActionResult Register(PostUserRegistration model) {
if (!ModelState.IsValid)
return BadRequest(ModelState); //<-- will extract errors from model state
//...code removed for brevity
return Ok();
}
A simple example of a unit test for the above method could look something like this...
[TestMethod]
public void Register_ReturnErrorsWithBadRequest_IfValidationFails() {
//Arrange
PostUserRegistration model = new PostUserRegistration {
Name = "Wai Yan Hein",
Email = "waiyanhein#gmail.com",
Password = ""
};
var accRepoMock = new Mock<IAccountRepo>();
var controller = new AccountsController(accRepoMock.Object);
controller.ModelState.AddModelError("", "Faking some model error");
//Act
var response = controller.Register(model) as InvalidModelStateResult; //<-- Note the cast here
//Assert
Assert.IsNotNull(response);
}
If the injected dependency is not going to be referenced/invoked in the method under test you could also forego the mock and inject null. That however is dependent on code not provided in the original question.
I have in my controller action (I removet try/catch block for more clear code):
[HttpPost]
public ActionResult SendMessage(ContactViewModel model)
{
if (ModelState.IsValid)
{
MailMessage message = new MailMessage();
var administrator = new MailAddress("xxxx#gmail.com");
var customer = new MailAddress(model.Email, model.Name);
message.Bcc.Add(customer);
message.Bcc.Add(administrator);
message.Subject = "Message Subject";
message.Body = model.Message;
SmtpClient client = new SmtpClient();
client.Send(message);
}
return PartialView("_ContactPartial", model);
}
The action works correctly, the info about host are in web.config.
I want create unit test's for this action. I created in my project with test web.config:
<configuration>
<system.net>
<mailSettings>
<smtp deliveryMethod="SpecifiedPickupDirectory">
<specifiedPickupDirectory pickupDirectoryLocation="C:\MailTest\"/>
</smtp>
</mailSettings>
</system.net>
</configuration>
And there is basic unit test:
[TestMethod]
public void ContactControllerTest_SendMessage_PartialView()
{
//Arrange
var controller = new ContactController();
//Act
var result = controller.SendMessage(contactViewModel) as PartialViewResult;
//Asset
Assert.AreEqual("_ContactPartial", result.ViewName");
}
In the line
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
I have exception: The SMTP host was not specified. Why it need it if in my web.config I selected SpecifiedPickupDirectory as delivery method?
All,
I'm developing and unit testing an interactive voice application using ASP.NET MVC 3 whose controllers return Views containing VoiceXML. I'd like to create unit tests that capture the actual VoiceXML output so I can schema-validate it.
My reading and testing have taken me to Scott H's FakeHttpContext that uses Moq, as well as several responses here. Everything compiles correctly, and I'm trying to do something like the following:
[TestMethod]
public void WelcomeTest1()
{
EmergencyController controller = new EmergencyController();
controller.ControllerContext = new ControllerContext(MvcMockHelpers.FakeHttpContext("~/Emergency/Welcome"), new RouteData(), controller);
ViewResult result = (controller.Welcome()) as ViewResult;
.
.
Assert.IsTrue(controller.ControllerContext.HttpContext.Response.OutputStream.Length > 0);
// assert schema validation on the output here
}
However, stepping through this, I can see that the Welcome view being called, but I'm looking for something in the Response.Output and not finding anything. The mock is set up as follows, in hope that setting CallBase to true would actually write something out. I found some code that I added to the FakeHttpContext constructor that supposedly invokes a StringWriter, but to no avail:
public static HttpContextBase FakeHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>() { CallBase = true };
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);
response.Setup(r => r.OutputStream).Returns(new MemoryStream());
response.Setup(r => r.Headers).Returns(new NameValueCollection());
var writer = new StringWriter();
var wr = new SimpleWorkerRequest("", "", "", "", writer);
HttpContext.Current = new HttpContext(wr);
return context.Object;
}
I'm sure I'm missing something obvious, but I'm stumped right now.
Thanks
Jim Stanley
Blackboard Connect
The result doesn't get populated in the ViewResult. In other words, the view isn't rendered by you calling return View() in your controller, rather mvc takes the viewresult and renders it at a later time in the request-sycle. What you need to do is to create a render-function for ViewResults, like this one:
using System;
using System.IO;
using System.Web.Mvc;
namespace CoPrice.Helpers
{
public static class ViewRendrer
{
public static string ToHtml(this ViewResult result, Controller controller)
{
controller.ViewData.Model = result.Model;
try
{
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, result.ViewName);
ViewContext context = new ViewContext(controller.ControllerContext, viewResult.View, result.ViewData, result.TempData, sw);
viewResult.View.Render(context, sw);
return sw.GetStringBuilder().ToString();
}
}
catch (Exception e)
{
return e.ToString();
}
}
}
}
Then you can do result.ToHtml(controller) to get the actual data (this works for RazorViews only I think, though I'm not sure, that's what I've used it for at least).