why My UserSession in mvc controller is Null? - asp.net-mvc

I have ServiceStack in my mvc project and I'm trying to share a session between ServiceStack and ASP MVC .. I follow all the steps from https://github.com/ServiceStack/ServiceStack/wiki/Sessions to share a session, but when I try to get the values of UserSession in my asp mvc controller it displays a NULL VAUE...why is Null? I have this code
AppHost.cs
{
ControllerBase<CustomUserSession>
public class CustomUserSession : AuthUserSession
{
public string CustomProperty1 { get; set; }
public string CustomProperty2 { get; set; }
}
public class AppHost
: AppHostBase
{
public AppHost() //Tell ServiceStack the name and where to find your web services
: base("StarterTemplate ASP.NET Host", typeof(HelloService).Assembly) { }
public override void Configure(Funq.Container container)
{
Plugins.Add(new SessionFeature());
container.Register<ICacheClient>(new MemoryCacheClient());
//Set JSON web services to return idiomatic JSON camelCase properties
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
//Configure User Defined REST Paths
Routes
.Add<Hello>("/hello")
.Add<Hello>("/hello/{Name*}");
//Uncomment to change the default ServiceStack configuration
//SetConfig(new EndpointHostConfig {
//});
//Enable Authentication
//ConfigureAuth(container);
//Register all your dependencies
container.Register(new TodoRepository());
//Set MVC to use the same Funq IOC as ServiceStack
ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
ServiceStackController.CatchAllController = reqCtx => container.TryResolve<HomeController>();
}
/* Uncomment to enable ServiceStack Authentication and CustomUserSession
private void ConfigureAuth(Funq.Container container)
{
var appSettings = new AppSettings();
//Default route: /auth/{provider}
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider(appSettings),
new FacebookAuthProvider(appSettings),
new TwitterAuthProvider(appSettings),
new BasicAuthProvider(appSettings),
}));
//Default route: /register
Plugins.Add(new RegistrationFeature());
//Requires ConnectionString configured in Web.Config
var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));
container.Register<IUserAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>()));
var authRepo = (OrmLiteAuthRepository)container.Resolve<IUserAuthRepository>();
authRepo.CreateMissingTables();
}
*/
public static void Start()
{
new AppHost().Init();
}
}
}
HomeController.com
public class HomeController : ControllerBase
{
public virtual ActionResult Index()
{
ViewBag.Message = "Sharing Sessions Btw SS and ASP MVC";
return View();
}
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(User request)
{
var user_Session = SessionFeature.GetOrCreateSession<CustomUserSession>(CacheClient);
return Json(user_Session);
}
So the user_Session is null ... can you help me?

Try inheriting from ServiceStackController, e.g:
public class HomeController : ServiceStackController<CustomUserSession>
{
[HttpPost]
public ActionResult Login(User request)
{
CustomUserSession userSession = base.UserSession;
return Json(userSession);
}
}
Also if you want to use a Custom UserSession (i.e. other than the AuthUserSession default) you need to tell ServiceStack how to create it, by specifying it in the AuthFeature constructor:
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CredentialsAuthProvider(appSettings),
}));

Related

Unit Test with Asp.Net Web Api and customer filter

I am working on the Unit Testing in Asp.Net Mvc Web Api.
I have 2 projects
1: Catalog.Api - This contains all the controllers
2: Catalog.UnitTests - This contains the Unit Test for controllers
All Controllers are Inherit with "ApiController" and every controller has custom filter [AuthenticationFilter]. Here is my values controller.
[AuthenticationFilter]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
And my custom is check the authorization token. Here it is
public class AuthenticationFilter: AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var request = actionContext.Request;
var authorization = request.Headers.Authorization;
if (authorization == null || authorization.Scheme != "Bearer")
{
ShowAuthenticationError(actionContext, "Authorization required");
return;
}
if (string.IsNullOrEmpty(authorization.Parameter))
{
ShowAuthenticationError(actionContext, "Missing Jwt Token");
return;
}
var token = authorization.Parameter;
var principal = AuthenticateToken(token);
if (principal == null)
{
ShowAuthenticationError(actionContext, "Invalid token");
return;
}
base.OnAuthorization(actionContext);
}
private static void ShowAuthenticationError(HttpActionContext filterContext, string message)
{
var responseDTO = new ResponseDTO() { Code = 401, Message = message };
filterContext.Response =
filterContext.Request.CreateResponse(HttpStatusCode.Unauthorized, responseDTO);
}
}
public class ResponseDTO
{
public int Code { get; set; }
public string Message { get; set; }
}
Now in the Unit Test project i have a class and unit test method.
[TestMethod]
public void CheckFilter()
{
try
{
var controller = new ValuesController();
var controllerContext = new HttpControllerContext();
var request = new HttpRequestMessage();
request.Headers.Add("Authorization", "bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6InVhbGkiLCJlbWFpbCI6InVhbGlAaW5yZWFjaGNlLmNvbSIsIm5iZiI6MTU2NDY0NjIyMSwiZXhwI");
controllerContext.Request = request;
controller.ControllerContext = controllerContext;
var result = controller.Get();
Assert.IsTrue(result.Any());
}
catch (Exception ex)
{
Assert.Fail();
}
}
I am calling my controller by adding reference of API project into my unit test project. So all controllers are available in the unit test project.
Issue is that when i call the values controller it always return the data. And when i remove the request and header so it is also returning the data but in that case that will be unauthorized.
I think my custom filter is not calling. How should that would be called and authenticate the user.
I check your question and configure that issue it is basically you are calling the controller directly.
Basically controller is a class and when you are calling that it is behaving like a simple class and call the method and send back the result. It is simple and clear
But in your situation you have project for your api so can do this.
[TestMethod]
public void CheckFilter()
{
try
{
var config = new HttpConfiguration();
// This is the resgister method which is written in you Api project. That code is after this method this method because i did the same thing to call my controller.
Catalog.Api.WebApiConfig.Register(config);
using (var server = new HttpServer(config))
{
var client = new HttpClient(server);
string url = "http://localhost:PortNumberOfProject/api/values";
var request = new HttpRequestMessage
{
RequestUri = new Uri(url),
Method = HttpMethod.Get
};
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "Your Token");
var response = await client.SendAsync(request);
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
catch (Exception ex)
{
Assert.Fail();
}
}
Here is the WebApi Register method of Api project which is used to register the Api and Routes.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Here is your controller as it is. And now debug your test and add a break point in your [AuthenticationFilter] and OnAuthorization method.
[AuthenticationFilter]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}

MVC HTTP Error 403.14 - Forbidden after create new record

1- AuthorizeUserAttribute.cs is class for costume authorize attribute
public class AuthorizeUserAttribute : AuthorizeAttribute
{
public string AccessLevel { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
return false;
if (this.AccessLevel.Contains("Admin"))
{
return true;
}
else return false;
}
2- this is my controller
[AuthorizeUser(AccessLevel = "Admin")]
public class ProductsController : Controller
{
private DataBaseContext db = new DataBaseContext();
public ActionResult Index()
{
var product = db.Product.Include(p => p.ProductGroup);
return View(product.ToList());
}
}
[AuthorizeUser(AccessLevel = "Admin")]
public ActionResult Create([Bind(Include = "Product_Id,ProductName,Description,PicUrl,Group_Id")] Product product)
{
if (ModelState.IsValid)
{
db.Product.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Group_Id = new SelectList(db.ProductGroups, "Group_Id", "GreoupName", product.Group_Id);
return View(product);
}
3-FilterConfig.cs in start_up folder
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
filters.Add(new AuthorizeUserAttribute());
}
}
4-Global.asax.cs
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
}
5- Admin1Controller.cs for login and etc...
[HttpPost]
public ActionResult Login(LoginViewModel model)
{
if (!ModelState.IsValid) //Checks if input fields have the correct format
{
return View(model); //Returns the view with the input values so that the user doesn't have to retype again
}
if(model.Email == "info#psmgroups.com" & model.Password == "#1234psm")
{
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name,"Admin" ),
new Claim(ClaimTypes.Email, "info#psmgroups.com"),
new Claim(ClaimTypes.Role,"Admin")
}, "ApplicationCookie");
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignIn(identity);
return Redirect(GetRedirectUrl(model.ReturnUrl));
}
ModelState.AddModelError("", "incorrect UserName or pass");
return View(model);
}
after create new product and return to products/ show HTTP Error 403.14 - Forbidden page. while write product/Index show correct page
First, there's no code here that actually ever sets the AccessLevel property on your custom attribute. Maybe you just didn't post it, but if this is all your code, then it's fairly obvious why this doesn't work: AccessLevel is always null, and therefore never contains the string "Admin".
That said, you don't even need a custom attribute here. AuthorizeAttribute already handles roles. It seems you're trying to implement some sort of parallel role-like functionality, but that's a waste of time. Just do:
[Authorize(Roles = "Admin")]
And call it a day.

ASP.Net vNext DbContext Dependency Injection multiple request issues.

I am attempting to use ASP.Net vNext, MVC, EF7, and the repository pattern (not the issue here, I don't think)...
The issue I'm having is that when multiple requests are made against the database, I'm getting the following error: "There is already an open DataReader associated with this Command which must be closed first."
Here's some code:
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
Configuration = new Configuration().AddJsonFile("config.json").AddEnvironmentVariables();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Register Entity Framework
services.AddEntityFramework(Configuration)
.AddSqlServer()
.AddDbContext<MyDbContext>();
services.AddSingleton<ILocationRepo, LocationRepo>();
services.AddSingleton<IStateRepo, StateRepo>();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
var testData = ActivatorUtilities.CreateInstance<TestData>(app.ApplicationServices);
testData.InitializeData();
}
}
The controller:
[Route("api/[controller]")]
public class LocationsController : Controller
{
private readonly ILocationRepo _repo;
public LocationsController(ILocationRepo repo)
{
_repo = repo;
}
// GET: api/locations
[HttpGet]
public List<Location> Get()
{
return _repo.All.ToList();
}
// GET api/locations/5
[HttpGet("{id}")]
public IActionResult Get(int id)
{
var ret = _repo.GetById(id);
if (ret == null)
return new HttpNotFoundResult();
return new ObjectResult(ret);
}
// POST api/locations
[HttpPost]
public IActionResult Post([FromBody]Locationvalue)
{
var ret = _repo.AddOrUpdate(value);
if (ret == null)
return new BadRequestResult();
return new ObjectResult(ret);
}
// PUT api/locations/5
[HttpPut("{id}")]
public IActionResult Put(int id, [FromBody]Location value)
{
var ret = _repo.AddOrUpdate(value);
if (id == 0 || ret == null)
return new BadRequestResult();
return new ObjectResult(ret);
}
// DELETE api/locations/5
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
var existing = _repo.GetById(id);
if (existing == null)
return new HttpNotFoundResult();
bool ret = _repo.TryDelete(id);
return new ObjectResult(ret);
}
}
The States repository:
public class StateRepo : IStateRepo
{
private readonly MyDbContext context;
public StateRepo(MyDbContext diContext)
{
context = diContext;
}
public IEnumerable<State> All
{
get
{
return context.States;
}
}
public State GetById(int id)
{
return context.States.FirstOrDefault(x => x.Id == id);
}
}
I have pretty much the same repo setup for Locations (with a few more methods, of course)... the problem comes in when I'm making simultaneous AJAX calls to my locations and states controllers. I would expect the DI for the context to handle such collisions, but it doesn't appear to be doing so. Is there another way to configure this to work correctly without having to go back to the old way of creating an instance of my context throughout my repos? Do I have anything configured incorrectly?
I don't claim to be a DI expert, but try registering your repositories with AddScoped instead of AddSingleton. I think you are getting the same instance of the repository for each request which probably has the same instance of your DbContext and DbContext is not thread safe.
Also, make sure you have MultipleActiveResultSets=true in your connectionstring. I think that can also cause the error you are seeing.

Orchard cms apicontroller that returns xml

I've been trying to implement David Hayden's Orchard CMS and ASP .NET Web API http://www.davidhayden.me/blog/orchard-cms-and-asp.net-web-api, but I'm running into problems, basically getting a page not found.
This is what I have:
Under my controllers
ContactViewModel.cs
namespace Sunkist.ContactManager.Controllers
{
public class ContactsController : ApiController
{
private readonly IContentManager _contentManager;
public ContactsController(IContentManager contentManager)
{
_contentManager = contentManager;
}
public IEnumerable<ContactViewModel> Get()
{
return _contentManager
.Query(VersionOptions.Published, "Contact")
.List()
.Select(c => new ContactViewModel(c));
}
public ContactViewModel Get(int id)
{
var contact = _contentManager.Get(id);
if (contact == null)
throw new HttpResponseException
(new HttpResponseMessage(HttpStatusCode.NotFound));
return new ContactViewModel(contact);
}
}
}
ViewModel folder
ViewModel.cs
namespace Sunkist.ContactManager.ViewModel
{
public class ContactViewModel
{
private Orchard.ContentManagement.ContentItem c;
public ContactViewModel(Orchard.ContentManagement.ContentItem c)
{
// TODO: Complete member initialization
this.c = c;
}
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
}
}
and migration
namespace Sunkist.ContactManager {
public class Migrations : DataMigrationImpl {
public int Create() {
// Creating table ContactRecord
SchemaBuilder.CreateTable("ContactRecord", table => table
.ContentPartRecord()
.Column("Name", DbType.String)
.Column("Address", DbType.String)
.Column("City", DbType.String)
);
return 1;
}
}
}
I'm new to both Orchard and .Net MVC, So I'm not sure what I'm doing wrong?
Double check the name of the module that contains the Web API controller.
Via VS GUI, I created a new web api controller class "TestController.cs" and could not find the correct endpoint url.
That is, until I looked at the Module.txt in the project I added the controller to. The project is "Orchard.Users", but the name in the Module.txt file is just "Users".
I was able to hit the endpoint at "http://example.com/api/users/test".
Noteworthy: I setup a route for this at ".../UsersApi", but the 'auto-magic' URL pattern still works. Code below. Also, I later added a new module to contain my api controller and it would not work until I enabled the module in the dashboard.
TestController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace Orchard.Users.Controllers
{
public class TestController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
HttpRoutes.cs
using System.Collections.Generic;
using Orchard.Mvc.Routes;
using Orchard.WebApi.Routes;
public class HttpRoutes : IHttpRouteProvider
{
public void GetRoutes(ICollection<RouteDescriptor> routes)
{
foreach (RouteDescriptor routeDescriptor in GetRoutes())
{
routes.Add(routeDescriptor);
}
}
public IEnumerable<RouteDescriptor> GetRoutes()
{
return new[] {
new HttpRouteDescriptor {
Name = "UsersApi",
Priority = -10,
RouteTemplate = "usersapi/{id}",
Defaults = new {
area = "Orchard.Users",
controller = "Test",
id = RouteParameter.Optional
},
}
};
}
}

Unit testing controller using MOQ . How to mock httpcontext

I am trying to test my Account controller by using Moq here is what i have done
Controller
private readonly IWebSecurity _webSecurity;
public AccountController(IWebSecurity webSecurity)
{
this._webSecurity = webSecurity;
}
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && _webSecurity.login(model))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
IWebSecurity
public interface IWebSecurity
{
bool login(LoginModel model);
}
public class WebSecurity : IWebSecurity
{
public bool login(LoginModel model)
{
return WebMatrix.WebData.WebSecurity.Login(model.UserName, model.Password, model.RememberMe);
}
}
MyTestClass
[AfterScenario]
public void OnAfterScenario() {
mockRepository.VerifyAll();
}
LoginModel loginModel;
AccountController _controller;
#region Initializing Mock Repository
readonly Mock<IWebSecurity> mockRepository = new Mock<IWebSecurity>(MockBehavior.Loose);
ViewResult viewResult;
#endregion
[Given]
public void Given_Account_controller()
{
_controller = new AccountController(mockRepository.Object);
}
[When]
public void When_login_is_called_with_LoginModel(Table table)
{
loginModel = new LoginModel
{
UserName = table.Rows[0][1],
Password = table.Rows[1][1]
};
mockRepository.Setup(x => x.login(loginModel)).Returns(true);
viewResult = (ViewResult)_controller.Login(loginModel, "/");
}
[Then]
public void Then_it_should_validate_LoginModel()
{
Assert.IsTrue(_controller.ModelState.IsValid);
}
[Then]
public void Then_it_should_return_default_view()
{
Assert.AreEqual(viewResult.ViewName, "Index");
}
But my test is failing and its giving expection when if come to Url.IsLocal in Redirect to Local method . so i think here is should mock my httpcontextbase and httpcontextrequestbase .
But don't know how to mock that .
Thanks in advance
You should mock the HttpContext. I wrote this helper to do this kind of things
public static Mock<HttpContextBase> MockControllerContext(bool authenticated, bool isAjaxRequest)
{
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.HttpMethod).Returns("GET");
request.SetupGet(r => r.IsAuthenticated).Returns(authenticated);
request.SetupGet(r => r.ApplicationPath).Returns("/");
request.SetupGet(r => r.ServerVariables).Returns((NameValueCollection)null);
request.SetupGet(r => r.Url).Returns(new Uri("http://localhost/app", UriKind.Absolute));
if (isAjaxRequest)
request.SetupGet(x => x.Headers).Returns(new System.Net.WebHeaderCollection { { "X-Requested-With", "XMLHttpRequest" } });
var server = new Mock<HttpServerUtilityBase>();
server.Setup(x => x.MapPath(It.IsAny<string>())).Returns(BasePath);
var response = new Mock<HttpResponseBase>();
response.Setup(r => r.ApplyAppPathModifier(It.IsAny<string>())).Returns((String url) => url);
var session = new MockHttpSession();
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Setup(c => c.Request).Returns(request.Object);
mockHttpContext.Setup(c => c.Response).Returns(response.Object);
mockHttpContext.Setup(c => c.Server).Returns(server.Object);
mockHttpContext.Setup(x => x.Session).Returns(session);
return mockHttpContext;
}
public class MockHttpSession : HttpSessionStateBase
{
private readonly Dictionary<string, object> sessionStorage = new Dictionary<string, object>();
public override object this[string name]
{
get { return sessionStorage.ContainsKey(name) ? sessionStorage[name] : null; }
set { sessionStorage[name] = value; }
}
public override void Remove(string name)
{
sessionStorage.Remove(name);
}
}
and in a test method you use it like that
private AccountController GetController(bool authenticated)
{
var requestContext = new RequestContext(Evoltel.BeniRosa.Web.Frontend.Tests.Utilities.MockControllerContext(authenticated, false).Object, new RouteData());
var controller = new CofaniController(cofaniRepository.Object, categorieRepository.Object, emailService.Object, usersService.Object)
{
Url = new UrlHelper(requestContext)
};
controller.ControllerContext = new ControllerContext()
{
Controller = controller,
RequestContext = requestContext
};
return controller;
}
[Test]
public void LogOn_Post_ReturnsRedirectOnSuccess_WithoutReturnUrl()
{
AccountController controller = GetController(false);
var httpContext = Utilities.MockControllerContext(false, false).Object;
controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);
LogOnModel model = new LogOnModel()
{
UserName = "someUser",
Password = "goodPassword",
RememberMe = false
};
ActionResult result = controller.LogOn(model, null);
Assert.IsInstanceOf(typeof(RedirectToRouteResult), result);
RedirectToRouteResult redirectResult = (RedirectToRouteResult)result;
Assert.AreEqual("Home", redirectResult.RouteValues["controller"]);
Assert.AreEqual("Index", redirectResult.RouteValues["action"]);
}
Hope it helps
In this particular issue you can simply overwrite controller's Url property with mocked UrlHelper class.
For HttpContext mocking, it might be good to inject HttpContextBase to your controller and configure your DI container to serve the proper one for you. It would ease mocking it later for testing purposes. I believe Autofac has some automatic way to configure container for ASP.NET-related classes like HttpContextBase.
EDIT
It seems you can't mock UrlHelper with Moq, as #lazyberezovsky wrote - you can mock only interfaces and virtual methods. But it does not stop you from writing your own mocked object. That's true you need to mock HttpContext, as it's required by UrlHelper constructor (actually, it's required by RequestContext constructor, which is required by UrlHelper constructor)... Moreover, IsLocalUrl does not use anything from context, so you do not have to provide any additional setup.
Sample code would look like that:
Controller:
public ActionResult Foo(string url)
{
if (Url.IsLocalUrl(url))
{
return Redirect(url);
}
return RedirectToAction("Index", "Home");
}
Tests:
[TestClass]
public class HomeControllerTests
{
private Mock<HttpContextBase> _contextMock;
private UrlHelper _urlHelperMock;
public HomeControllerTests()
{
_contextMock = new Mock<HttpContextBase>();
_urlHelperMock = new UrlHelper(new RequestContext(_contextMock.Object, new RouteData()));
}
[TestMethod]
public void LocalUrlTest()
{
HomeController controller = new HomeController();
controller.Url = _urlHelperMock;
RedirectResult result = (RedirectResult)controller.Foo("/");
Assert.AreEqual("/", result.Url);
}
[TestMethod]
public void ExternalUrlTest()
{
HomeController controller = new HomeController();
controller.Url = _urlHelperMock;
RedirectToRouteResult result = (RedirectToRouteResult)controller.Foo("http://test.com/SomeUrl");
Assert.AreEqual("Index", result.RouteValues["action"]);
Assert.AreEqual("Home", result.RouteValues["controller"]);
}
}

Resources