I am trying to Mock a HttpResponse for a ConrollerContext object using MVC 4, Moq 4 and C# 4.5
My code is here:
var context = new Mock<HttpContextBase>();
var response = new Mock<HttpResponseBase>();
context.SetupProperty(c => c.Response = response);
I have tried using Setup().Returns() and SetupGet() but I keep getting the following error:
"Property or Indexer 'System.Web.HttpContextBase.Response' cannot be assigned to -- it is read only.
I have tried Googling this and searching on this website, but I can't seem to find the answer.
For get-only properties, use SetupGet(..), and specify a .Returns(..) clause with the return value to inject:
var context = new Mock<HttpContextBase>();
var response = new Mock<HttpResponseBase>();
context.SetupGet(c => c.Response)
.Returns(response.Object);
It seems that I did not pass the correct object into the Returns() method. I should have passed the mock's Object property.
Here is the correct code:
var context = new Mock<HttpContextBase>();
var response = new Mock<HttpResponseBase>();
context.Setup(c => c.Response).Returns(response.Object);
Related
Trying to get a basic Elasticsearch Count to work in my netcore API.
Following the documentation I believe this should work-
var node = new Uri(host);
var settings = new ConnectionSettings(node);
settings.DefaultIndex("foo/bar");
var client = new ElasticClient(settings);
var x = await client.CountAsync<dynamic>();
return x.Count;
There is a problem with this, however. The DefaultIndex is encoding slashes (/foo%2Fbar/object/_count). I'm getting a no_index_found_exception exception.
It seems like an easy problem to fix, but I can't find the documentation for this.
How do I prevent uri encoding on my DefaultIndex?
To specify multiple indices as the default index, separate them with a comma. NEST will URI encode the comma
var defaultIndex = "users,posts";
var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.DefaultIndex(defaultIndex)
.DefaultTypeName("doc");
var client = new ElasticClient(settings);
var searchResponse = client.Search<object>(s => s);
Will send a search request to
POST http://localhost:9200/users%2Cposts/doc/_search
I am trying to test a method in an .ashx. (Yes, an ASP.NET webform site.)
private Mock<HttpContextBase> moqContext;
private Mock<HttpRequestBase> moqRequest;
private Mock<HttpSessionStateBase> moqSession;
[Test]
public void ShouldDownloadFile()
{
moqContext = new Mock<HttpContextBase>();
moqRequest = new Mock<HttpRequestBase>();
moqSession = new Mock<HttpSessionStateBase>();
moqRequest.Setup(x => x.Params["PrintId"]).Returns("1981");
moqSession.Setup(x => x["VoucherId"]).Returns("164282");
moqContext.Setup(x => x.Request).Returns(moqRequest.Object);
moqContext.SetupGet(x => x.Session).Returns(moqSession.Object);
var handler = new VoucherPrintServices();
handler.DownloadReport(moqContext.Object);
}
The test failed at
moqRequest.Setup(x => x.Params["PrintId"]).Returns("1981");
saying,
Invalid setup on a non-virtual (overridable in VB) member: x => x.Params["PrintId"]
But then what is the correct way to mock an HttpContext with session and request parameter values?
As the message says, that accessor is not virtual so it cannot be overridden/mocked for the test.
The NameValueCollection.Params property however is virtual, which means you can replace it with an actual NameValueCollection that contains that key.
var parameters = new NameValueCollection();
parameters.Add("PrintId", "1981");
moqRequest.Setup(x => x.Params).Returns(parameters);
I am writing a similar testing framework like this one: https://github.com/ivaylokenov/MyTested.WebApi but for ASP.NET 5 MVC 6 (using 6.0.0-beta8).
I have a hard time trying to invoke the controller's TryValidateModel to validate the ModelState of a given object. It worked without any pain on ASP.NET Web API 2 controller. Which properties should I instantiate on the controller class in order for the method to validate with the default conventions. I receive either null reference exception or valid model state no matter the model and the property attributes.
I've been searching in the MVC source code for a couple of hours with no luck. I have tried to set the AddMvc() services on the controller - not working for me. I even tried their testing controller type located HERE (the GetController private method) but with no luck. Here is the source I am currently using:
// TODO: for real this is how we configure controller?
var detailsProviders = new IMetadataDetailsProvider[]
{
new DefaultBindingMetadataProvider(new ModelBindingMessageProvider
{
MissingBindRequiredValueAccessor = name => $"A value for the '{ name }' property was not provided.",
MissingKeyOrValueAccessor = () => $"A value is required.",
ValueMustNotBeNullAccessor = value => $"The value '{ value }' is invalid.",
}),
new DefaultValidationMetadataProvider(),
new DataAnnotationsMetadataProvider(),
// new DataMemberRequiredBindingMetadataProvider(), TODO: not available in version 8 but it is in the source code of MVC
};
var compositeDetailsProvider = new DefaultCompositeMetadataDetailsProvider(detailsProviders);
var metadataProvider = new DefaultModelMetadataProvider(compositeDetailsProvider);
var httpContext = new DefaultHttpContext();
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var viewData = new ViewDataDictionary(metadataProvider, new ModelStateDictionary());
var tempData = new TempDataDictionary(new HttpContextAccessor(), new SessionStateTempDataProvider());
var bindingContext = new ActionBindingContext()
{
ModelBinder = new GenericModelBinder(),
ValueProvider = new CompositeValueProvider(new IValueProvider[0]),
InputFormatters = new List<IInputFormatter>(),
ValidatorProvider = new DataAnnotationsModelValidatorProvider(
options: null,
stringLocalizerFactory: null)
};
this.Controller.ActionContext = actionContext;
this.Controller.BindingContext = bindingContext;
this.Controller.MetadataProvider = metadataProvider;
this.Controller.ViewData = viewData;
this.Controller.TempData = tempData;
this.Controller.ObjectValidator = new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider);
Any help will be appreciated. Thank you in advance!
Turns out it is actually a bug in ASP.NET MVC and the team will fix it for the next RC2 version: https://github.com/aspnet/Mvc/issues/3586
Goal: test that a given url returns a given controller function.
In the process, I've broken into the routing system and I can't figure out how to test routes (or, for that matter, find the controller corresponding to the route :-/).
Sample code, which doesn't work:
[Test]
public void kick_the_tires()
{
var rc = new RouteCollection();
Infrastructure.RouteRegistry.RegisterRoutes(rc);
// get the route corresponding to name.
var got = rc["name"];
var expected = //What? foo is an internal type that can't be instantiated.
Assert.AreEqual(foo, frob);
}
edit: Using the linked blog post from Simon for the stub class.
[TestCase("/", "~/", "Home", "Index")]
[TestCase("/", "api/command", "Other", "command")]
internal void stub_mocker(string apppath, string route, string expected_controller,\
string expected_action)
{
var rc = new RouteCollection();
Infrastructure.RouteRegistry.RegisterRoutes(rc);
var httpmock = new StubHttpContextForRouting(
appPath: apppath,
requestUrl: route);
// this always returns null for everything but the Index case.
var routeData = rc.GetRouteData(httpmock);
var controller = routeData.Values["controller"];
var action = routeData.Values["action"];
Assert.AreEqual(expected_controller, controller);
Assert.AreEqual(expected_action, action);
}
All you are testing right now is if the routes are added to the collection, by accessing it by the route name, and not if the expected route will return given a virtual path. You need to obtain the route data as returned by the RouteCollection with a HttpContext.
Best way would be to use a mock or a stub for the HttpContext (or HttpContextBase) and call the RouteCollection's GetRouteData(HttpContextBase) method and inspect the route data.
There is a good example of this in Brad Wilson's blog:
http://bradwilson.typepad.com/blog/2010/07/testing-routing-and-url-generation-in-aspnet-mvc.html
Edit:You cannot get a controller instance from the RouteData itself. However, RouteData should give you enough information to know which controller will be instantiated. For example, if you have a controller at MyProject.Controllers.HomeController with an action Home, this should hold true in your test (using xUnit and Moq):
// Prepare
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
context.SetupGet(c => c.Request).Returns(request.Object);
context.SetupGet(c => c.Response).Returns(response.Object);
context.SetupGet(c => c.Session).Returns(session.Object);
context.SetupGet(c => c.Server).Returns(server.Object);
request.SetupGet(r => r.HttpMethod).Returns("GET");
request.SetupGet(r => r.PathInfo).Returns(String.Empty);
request.SetupGet(r => r.AppRelativeCurrentExecutionFilePath).Returns("~/Home");
var expectedHandler = typeof (HomeController).GetMethod("Index", Type.EmptyTypes);
var data = RouteTable.Routes.GetRouteData(context.Object);
Assert.NotNull(data);
var handler = (MethodInfo) data.DataTokens["actionMethod"];
Assert.Equal(expectedHandler, handler);
I've had prety good experience with MVCContrib's Testhelper
Take a look at this test of testhelper.
Saves a lot of hassles around stubbing HttpContext etc.
Also if you are on MVC4, have a look at this Nuget package which is a fork for MVC4.
As suggested by (among others) Kazi Manzur Rashid in this blog post, I am using ActionFilterAttributes to transfer model state from one request to another when redirecting.
However, I find myself unable to write a unit test that test the behavior of these attributes. As an example, this what I want the test for the ImportModelStateAttribute to do:
Setup the filterContext so that TempData[myKey] contains some fake "exported" ModelState (that is, a ModelStateDictionary I create myself, and add one error to)
Make ModelState contain one model error.
Call OnActionExecuting.
Verify the two dictionaries are merged, and ModelState now contains both errors.
I'm at a loss already on the first step.
EDIT:
Yes, I've tried mocking ActionFilterAttribute with Moq, but I get errors stating
Invalid setup on non-overridable member
for both TempData and ModelState.
Tomas, You do not have to mock the filterContext, you can create the real object for testing the action filter, the same goes for the model state, these are poco objects. Only thing that you have to mock is the HttpContext (if needed).
[Fact]
public void Should_import_complete_view_data()
{
var attribute = new ImportViewDataFromTempDataAttribute();
var httpContext = new Mock<HttpContextBase>();
var requestContext = new RequestContext(httpContext.Object, new RouteData());
var previousModel = new object();
var previousViewData = new ViewDataDictionary(previousModel) {{"foo", "bar"}};
previousViewData.ModelState.AddModelError("foo", "bar");
var controller = new Mock<ControllerBase>();
controller.Object.ViewData = new ViewDataDictionary();
controller.Object.TempData = new TempDataDictionary { { attribute.Key, previousViewData } };
var controllerContext = new ControllerContext(requestContext, controller.Object);
var actionContext = new ActionExecutingContext(controllerContext, new Mock<ActionDescriptor>().Object, new Dictionary<string, object>());
attribute.OnActionExecuting(actionContext);
Assert.True(actionContext.Controller.ViewData.ContainsKey("foo"));
Assert.True(actionContext.Controller.ViewData.ModelState.ContainsKey("foo"));
Assert.Same(previousModel, actionContext.Controller.ViewData.Model);
}