AssertionException: Value for parameter didn't match - asp.net-mvc

I've not used the MvcContrib for unit testing before and I'm having a bit of trouble running some of the tests.
I have the following test method:
[TestMethod]
public void Create_GET_Route_Maps_To_Action()
{
RouteData getRouteData = "~/Interface/Pages/Create".WithMethod(HttpVerbs.Get);
getRouteData.DataTokens.Add("Popup", "true");
getRouteData.DataTokens.Add("WebDirectoryId", "99");
getRouteData.DataTokens.Add("LocaleId", "88");
getRouteData.DataTokens.Add("LayoutId", "77");
getRouteData.ShouldMapTo<PagesController>(c => c.Create(true, 99, 88, 77));
}
Which matches to the following method in my Controller
[HttpGet]
[Popup]
public ViewResult Create(bool? popup, int? webDirectoryId, int? localeId, int? layoutId)
{
PageCreateViewModel pageCreateViewModel = new PageCreateViewModel
{
WebDirectories = GetChildDirectories(pageService.GetAllDirectories().Where(d => d.IsActive).Where(d => d.ParentId == null), ""),
Layouts = Mapper.Map<List<SelectListItem>>(pageService.GetAllLayouts().OrderBy(l => l.Filename)),
Locales = localizationService.GetAllLocales().Where(l => l.IsActive).OrderBy(l => l.LocaleName).Select(l => new SelectListItem { Text = string.Format("{0} ({1})", l.LocaleName, l.IETFLanguageTag), Value = l.LocaleId.ToString() })
};
return View(pageCreateViewModel);
}
I get the following error and I'm at a loss to figure out why.
MvcContrib.TestHelper.AssertionException: Value for parameter 'popup' did not match: expected 'True' but was ''; no value found in the route context action parameter named 'popup' - does your matching route contain a token called 'popup'?

The token names are case sensitive and should match the names of your action parameters and you need to use the Values collection instead of the DataTokens:
So because your action looks like this:
Create(bool? popup, int? webDirectoryId, int? localeId, int? layoutId)
You need to use the same lower-case token names and Values collection:
getRouteData.Values.Add("popup", "true");
getRouteData.Values.Add("webDirectoryId", "99");
getRouteData.Values.Add("localeId", "88");
getRouteData.Values.Add("layoutId", "77");

Related

Define MVC Route by name in .net Core

I have 2 routes defined:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("api", "{controller=Home}/api/v1/{action=Index}/{id?}");
});
In the controller, if I don't specify a route in the controller it will use either one. Both urls work:
https://myapp/mymodel/api/v1/id/123
https://myapp/mymodel/id/123
I want it to work only with the first url, but if add for example [Route("api")] to the controller none of the above routes work.
[Route("api")] //with this line it returns 404
public mymodel ID(int? id)
{
//some code
}
From the official doc :
Route names can be used to generate a URL based on a specific route. Route names have no impact on the URL matching behavior of routing and are only used for URL generation. Route names must be unique application-wide.
Here is a workaround on customizing a actionfilter attribute that checks if the url matches the route template for api , you could refer to:
ApiRouteTemplateAttribute
public class ApiRouteTemplateAttribute:ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var routeTemplate = "{controller=Home}/api/v1/{action=Index}/{id?}";
var template = TemplateParser.Parse(routeTemplate);
var matcher = new TemplateMatcher(template, GetDefaults(template));
var routeValues = new RouteValueDictionary();
string LocalPath = context.HttpContext.Request.Path;
var result = matcher.TryMatch(LocalPath, routeValues);
//if the match is false ,return a exception information.
if (!result)
{
context.Result = new BadRequestObjectResult(new Exception("The url is incorrect!"));
}
}
private RouteValueDictionary GetDefaults(RouteTemplate parsedTemplate)
{
var result = new RouteValueDictionary();
foreach (var parameter in parsedTemplate.Parameters)
{
if (parameter.DefaultValue != null)
{
result.Add(parameter.Name, parameter.DefaultValue);
}
}
return result;
}
}
Controller
[ApiRouteTemplate]
public Exam ID(int? id)
{
return _context.Exams.Find(id);
}
Result

MVC Attribute routing with Url.Action not resolving route

I cannot get #Url.Action to resolve to the url I am expecting based on the attribute route I have applied:
My action (SearchController but with [RoutePrefix("add")])
[Route("{searchTerm}/page/{page?}", Name = "NamedSearch")]
[Route("~/add")]
public ActionResult Index(string searchTerm = "", int page = 1)
{
...
}
Call to Url.Action
#Url.Action("Index", new { controller = "Search", searchTerm = "replaceMe", page = 1 })
This results in a url of
/add?searchTerm=replaceMe&page=1
I would expect
/add/replaceMe/page/1
If I type the url manually then it resolves to the correct action with the correct parameters. Why doesn't #Url.Action resolve the correct url?
Since you have a name for your pretty route definition, you may use the RouteUrl method.
#Url.RouteUrl("NamedSearch", new { searchTerm = "replaceMe", page = 1})
And since you need add in the url, you should update your route definition to include that in the url pattern.
[Route("~/add")]
[Route("~/add/{searchTerm?}/page/{page?}", Name = "NamedSearch")]
public ActionResult Index(string searchTerm = "", int page = 1)
{
// to do : return something
}
Routes are order sensitive. However, attributes are not. In fact, when using 2 Route attributes on a single action like this you may find that it works on some compiles and not on others because Reflection does not guarantee an order when analyzing custom attributes.
To ensure your routes are entered into the route table in the correct order, you need to add the Order property to each attribute.
[Route("{searchTerm}/page/{page?}", Name = "NamedSearch", Order = 1)]
[Route("~/add", Order = 2)]
public ActionResult Index(string searchTerm = "", int page = 1)
{
return View();
}
After you fix the ordering problem, the URL resolves the way you expect.
#Url.Action("Index", new { controller = "Search", searchTerm = "replaceMe", page = 1 })
// Returns "/add/replaceMe/page/1"
To return full URL use this
#Url.Action("Index", new { controller = "Search", searchTerm = "replaceMe", page = 1}, protocol: Request.Url.Scheme)
// Returns "http://yourdomain.com/add/replaceMe/page/1"
Hope this helps someone.

Issue while testing route entries using mvccontrib test helper in MVC4

I am trying to write test methods for Route entries and in my route config there were a lot of entries for different action. I am using MvcContrib.TestHelper for Testing . I am new to MVC as well as TDD. Kindly help me to solve this issue. My test case is failing and what can I do to make it pass .
Route Entry
routes.MapRoute(
name: "X1Details",
url: "X1/{X1ID}",
defaults: new { controller = "X1", action = "Index", X1ID = "X1ID" }
);
X1Controller
public ActionResult Index(int? instanceID = 0, string InfoMsg ="")
{
}
Test Method
[Test Method]
public void Should_Route_X1Index()
{
"~/X1/"
.ShouldMapTo<X1Controller>(action => action.Index(null, ""));
}
Error
Test method
XXX.Tests.MVCContibRouteMapTest.Should_Route_X1IndexOf
threw exception: MvcContrib.TestHelper.AssertionException: Value for
parameter 'InfoMsg' did not match: expected '' but was ''; no value
found in the route context action parameter named 'InfoMsg' - does
your matching route contain a token called 'InfoMsg'?
Thanks in advance.
I've found this approach to work, which should allow your controllers to remain the same
[Test Method]
public void Should_Route_X1Index()
{
var routeData = "~/X1/".Route();
routeData.Values["InfoMsg "] = "";
routeData.Values["instanceID"] = "0";
routeData.ShouldMapTo<X1Controller>(action => action.Index(0, ""));
}
Default values don't work well with MVCContrib helper. You could modify your controller action like so:
public ActionResult Index(int? instanceID, string InfoMsg)
{
...
}
and then:
"~/X1".ShouldMapTo<X1Controller>(action => action.Index(null, null));

Incorrect URL in passing value from Html.AtionLink to controller

My Controller:-ForumController
Action:Topic
ViewPage to pass parameter is
<%= Html.ActionLink("Topic", "Topic", "Forum", new { userid = "1" }, null)%>
Controller
public ActionResult Topic(String userid)
{
var topics = new topic { userId = userid };
return View(topics);
}
The parameter is getting passed but instead of getting URL Forum/Topic/1,I am getting Forum/Topic?userid=1;
Can anyone help me out
I suspect that you used the default route in Global.asax in which you have {controller}/{action}/{id}.
So you should use the same name (id):
<%= Html.ActionLink("Topic", "Topic", "Forum", new { id = "1" }, null)%>
and then:
public ActionResult Topic(String id)
{
var topics = new topic { userId = id };
return View(topics);
}
If you don't want to use {id} but some other token make sure you adjust your routes accordingly.

Using Moq to test methods that accept non-primative arguments

I'm trying to write a test for an ASP.Net MVC controller action.
I'd like to test that the action invokes a particular method on an injected service, so I'm mocking the service and using .Verify.
So in the simple case, I have the following action in my controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string title)
{
_cmsService.AddPage(title);
return View("Edit");
}
using the service interface...
public interface ICmsService
{
void AddPage(string request);
}
and the following test...
//Arrange
const string pageTitle = "Test Page";
var cmsService = new Mock<ICmsService>();
cmsService.Setup(service => service.AddPage(pageTitle));
var controller = new PageController(cmsService.Object);
//Act
var result = controller.Create(pageTitle) as ViewResult;
//Assert
cmsService.Verify(service => service.AddPage(pageTitle), Times.Once());
Now I want to refactor my service operation to use request and response objects...
public interface ICmsService
{
CmsServiceAddPageResponse AddPage(CmsServiceAddPageRequest request);
}
So I change my action accordingly...
public ActionResult Create(string title)
{
var request = new CmsServiceAddPageRequest()
{
PageName = title
};
var response = _cmsService.AddPage(request);
return View("Edit");
}
and also my test...
//Arrange
const string pageTitle = "Test Page";
var cmsService = new Mock<ICmsService>();
var request = new CmsServiceAddPageRequest() {PageName = pageTitle};
cmsService.Setup(service => service.AddPage(request));
var controller = new PageController(cmsService.Object);
//Act
var result = controller.Create(pageTitle) as ViewResult;
//Assert
cmsService.Verify(service => service.AddPage(request), Times.Once());
But now when I run the test, I get the following message...
TestCase 'Web.Test.PageControllerTest.CreateNewPagePost'
failed: Moq.MockException :
Invocation was performed more than once on the mock: service => service.AddPage(value(Web.Test.PageControllerTest+<>c__DisplayClass1).request)
at Moq.Mock.ThrowVerifyException(IProxyCall expected, Expression expression, Times times)
at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
at Moq.Mock.Verify[T,TResult](Mock mock, Expression`1 expression, Times times, String failMessage)
at Moq.Mock`1.Verify[TResult](Expression`1 expression, Times times)
PageControllerTest.cs(67,0): at Web.Test.PageControllerTest.CreateNewPagePost()
What should I be doing to test a method that accepts a non-primitive type?
Thanks
Sandy
I think a better alternative to the first answer would be to implement a custom matcher rather than change code to match your testing framework. From:http://code.google.com/p/moq/wiki/QuickStart
// custom matchers
mock.Setup(foo => foo.Submit(IsLarge())).Throws<ArgumentException>();
...
public string IsLarge()
{
return Match<string>.Create(s => !String.IsNullOrEmpty(s) && s.Length > 100);
}
If you override Equals in the CmsServiceAddPageRequest object it should compare them correctly.

Resources