I want to know how to unit test my controller when it inherits from a base controller that is dependent on HttpContext. Below is my inherited controller called BaseInterimController. And below that is the AccountController method that I wish to Unit Test. We are using MOQ.
public abstract class BaseInterimController : Controller
{
#region Properties
protected string InterimName
{
get { return MultiInterim.GetInterimName(InterimIdentifier); }
}
internal virtual string InterimIdentifier
{
get { return System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["InterimIdentifier"].ToString(); }
}
}
public class AccountController : BaseInterimController
{
[HttpPost]
[AllowAnonymous]
[ValidateInput(false)]
[Route(#"{InterimIdentifier:regex([a-z]{7}\d{4})}/Account/Signin")]
public ActionResult Signin(LoginViewModel model)
{
if (ModelState.IsValid)
{
var identity = Authentication.SignIn(model.Username,
model.Password) as LegIdentity;
if (identity != null && identity.IsAuthenticated)
{
return Redirect(model.ReturnUrl);
}
else
{
// Sign in failed
ModelState.AddModelError("",
Authentication.ExternalSignInFailedMessage);
}
}
return View(model);
}
}
Coupling your controller to HttpContext can make your code very difficult to test because during unit tests HttpContext is null unless you try to mock it; which you shouldn't really do. Don't mock code you don't own.
Instead try abstracting the functionality you want to get from HttpContext into something you have control over.
this is just an example. You can try to make it even more generic if needed. I will focus on your specific scenario.
You are calling this directly in your controller
System.Web.HttpContext.Current.Request
.RequestContext.RouteData.Values["InterimIdentifier"].ToString();
When what you are really after is the ability to get that InterimIdentifier value. Something like
public interface IInterimIdentityProvider {
string InterimIdentifier { get; }
}
public class ConcreteInterimIdentityProvider : IInterimIdentityProvider {
public virtual string InterimIdentifier {
get { return System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["InterimIdentifier"].ToString(); }
}
}
which can later be implemented in a concrete class and injected into your controller provided you are using Dependency Injection.
Your base controller will then look like
public abstract class BaseInterimController : Controller {
protected IInterimIdentityProvider identifier;
public BaseInterimController(IInterimIdentityProvider identifier) {
this.identifier = identifier;
}
protected string InterimName {
get { return MultiInterim.GetInterimName(identifier.InterimIdentifier); }
}
//This can be refactored to the code above or use what you had before
//internal virtual string InterimIdentifier {
// get { return identifier.InterimIdentifier; }
//}
}
public class AccountController : BaseInterimController
{
public AccountController(IInterimIdentityProvider identifier)
: base(identifier){ }
[HttpPost]
[AllowAnonymous]
[ValidateInput(false)]
[Route(#"{InterimIdentifier:regex([a-z]{7}\d{4})}/Account/Signin")]
public ActionResult Signin(LoginViewModel model)
{
if (ModelState.IsValid)
{
var identity = Authentication.SignIn(model.Username,
model.Password) as LegIdentity;
if (identity != null && identity.IsAuthenticated)
{
return Redirect(model.ReturnUrl);
}
else
{
// Sign in failed
ModelState.AddModelError("",
Authentication.ExternalSignInFailedMessage);
}
}
return View(model);
}
}
This allows implemented controllers to not be dependent on HttpContext which will allow for better unit testing as you can easily mock/fake IInterimIdentityProvider interface using Moq to return what you want during tests.
[TestMethod]
public void Account_Controller_Should_Signin() {
//Arrange
var mock = new Mock<IInterimIdentityProvider>();
mock.Setup(m => m.InterimIdentifier).Returns("My identifier string");
var controller = new AccountController(mock.Object);
var model = new LoginViewModel() {
Username = "TestUser",
Password = ""TestPassword
};
//Act
var actionResult = controller.Signin(model);
//Assert
//...assert your expected results
}
Related
This is my controller
BusinessLayer.Lookup.Lookup_Rooms RoomsBL = new BusinessLayer.Lookup.Lookup_Rooms();
[HttpPost]
public ActionResult CreateRoom(UMIS_Portal_BackEnd.Areas.StudentAcadimic.Models.RoomLocationModel room) {
if (ModelState.IsValid)
{
try
{
// TODO: Add insert logic here
int? Serial = RoomsBL.Lookup_RoomInsert(room.BuildingFloorID, room.RoomName, room.Min_Capacity, room.Max_Capacity);
}
catch (Exception error)
{
ViewBag.Messages = error.InnerException.Message;
}
}
return View();
}
I only want to test that there is view exist for this action result so i write this code
[TestMethod()]
public void CreateRoomTestPost()
{
LookupRoomsController controller = new LookupRoomsController();
UMIS_Portal_BackEnd.Areas.StudentAcadimic.Models.RoomLocationModel room = new UMIS_Portal_BackEnd.Areas.StudentAcadimic.Models.RoomLocationModel();
ViewResult viewResult = controller.CreateRoom(room) as ViewResult;
Assert.IsNotNull(viewResult);
}
I want the test to skip calling
int? Serial = RoomsBL.Lookup_RoomInsert(room.BuildingFloorID, room.RoomName, room.Min_Capacity, room.Max_Capacity);
on the main controller action.
The problem is that the controller is tightly coupled to implementation details
BusinessLayer.Lookup.Lookup_Rooms RoomsBL = new BusinessLayer.Lookup.Lookup_Rooms();
instead of having abstractions explicitly injected into it. It is not the controller's responsibility to create its dependencies. (Single Responsibility Principle (SRP) and Separations of Concerns (Soc))
The Lookup_Rooms needs to be abstracted
public interface ILookup_Rooms {
//...
}
public class Lookup_Rooms : ILookup_Rooms {
//...
}
and the abstraction explicitly injected into the controller.
private readonly BusinessLayer.Lookup.ILookup_Rooms RoomsBL;
//CTOR
public LookupRoomsController(ILookup_Rooms RoomsBL) {
this.RoomsBL = RoomsBL;
}
[HttpPost]
public ActionResult CreateRoom(RoomLocationModel room) {
if (ModelState.IsValid) {
try {
// TODO: Add insert logic here
int? Serial = RoomsBL.Lookup_RoomInsert(room.BuildingFloorID, room.RoomName, room.Min_Capacity, room.Max_Capacity);
} catch (Exception error) {
ViewBag.Messages = error.InnerException.Message;
}
}
return View();
}
That way a mocked one can be made to perform no action when Lookup_RoomInsert is invoked in an isolated unit test.
[TestMethod()]
public void CreateRoomTestPost() {
// Arrange
ILookup_Rooms mock = Mock.Of<ILookup_Rooms>(); //using MOQ
LookupRoomsController controller = new LookupRoomsController(mock);
RoomLocationModel room = new RoomLocationModel();
// Act
ViewResult viewResult = controller.CreateRoom(room) as ViewResult;
// Assert
Assert.IsNotNull(viewResult);
}
Finally, the abstraction and implementation should be registered with a DI container so that the actual implementation can be properly injected into the controller when the system is running in production.
I've an api with few methods in it. I need to authenticate user with Windows Authentication before it hits any of the controller methods. To achieve this, I've written a private method to get the UserIdentity and assign it to a private bool variable.
Each of the controller methods will first check the bool to verify the user and if it fails, will throw 401 back to the front end.
Whilst writing unit test case to the controller methods, I'm not sure how to mock the UserIdentity or should I update the controller in a way to allow the testing of UserIdentity
Controller :
public class DefaultController : ApiController
{
private bool isUsrAuthenticated;
public DefaultController()
{
AuthenticateUser();
}
private void AuthenticateUser()
{
isUsrAuthenticated = User.Identity.IsAuthenticated;
}
[HttpGet]
public IHttpActionResult GetProducts()
{
if (isUsrAuthenticated) { // do something }
else { // throw 401 }
}
[HttpPost]
public IHttpActionResult Update()
{
if (isUsrAuthenticated) { // do something }
else { // throw 401 }
}
}
The Unit Test results always return 401.
At run-time the ApiController.User property is set well after the constructor of the controller is invoked. That would mean that your current flow of calling it in the constructor like
public DefaultController() {
AuthenticateUser();
}
is flawed and will not provide the expected behavior.
Lets fix the code first, then look at the unit test.
Use a property instead like
private bool IsUserAuthenticated {
get {
return User.Identity.IsAuthenticated;
}
}
and refactor the code accordingly
public class DefaultController : ApiController {
private bool IsUserAuthenticated {
get {
return User.Identity.IsAuthenticated;
}
}
[HttpGet]
public IHttpActionResult GetProducts() {
if (IsUserAuthenticated) { // do something }
else { // throw 401 }
}
[HttpPost]
public IHttpActionResult Update() {
if (IsUserAuthenticated) { // do something }
else { // throw 401 }
}
//...
}
Now, for testing the ApiController, the User property can be set while arranging the unit test in order for the test to be exercised as expected
For example
[TestMethod()]
public void DefaultController_Should_GetProducts() {
//Arrange
var username = "name_here";
var userId = 2;
var identity = new GenericIdentity(username, "");
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, username));
var principal = new GenericPrincipal(identity, roles: new string[] { });
var user = new ClaimsPrincipal(principal);
var controller = new DefaultController() {
User = user //<-- Set the User on the controller directly
};
//Act
var actionResult = controller.GetProducts();
//Assert
//...
}
I'm trying to test my project. I have never used tests before and I am starting to learn I would like a help, in the simplest case I want test this public ActionResult Index() but I don't know how to Inject those dependencies.
Controller:
Controller:
public class WorkPlacesController : Controller
{
private readonly IWorkPlaceService workPlaceService;
public WorkPlacesController(IWorkPlaceService workPlaceService)
{
this.workPlaceService = workPlaceService;
}
// GET: WorkPlaces
public ActionResult Index()
{
var workPlaces = workPlaceService.GetWorkPlaces(includedRelated:
true);
return View(workPlaces);
}
}
Here is my Service
Service
public class WorkPlaceService : IWorkPlaceService
{
private readonly IWorkPlaceRepository workPlacesRepository;
private readonly IUnitOfWork unitOfWork;
public WorkPlaceService(IWorkPlaceRepository workPlacesRepository, IUnitOfWork unitOfWork)
{
this.workPlacesRepository = workPlacesRepository;
this.unitOfWork = unitOfWork;
}
}
public interface IWorkPlaceService
{
IEnumerable<WorkPlace> GetWorkPlaces(string workPlaceDescription = null, bool includedRelated = true);
}
And my Repository
Repository
public class WorkPlaceRepository : RepositoryBase<WorkPlace>, IWorkPlaceRepository
{
public WorkPlaceRepository(IDbFactory dbFactory)
: base(dbFactory) { }
public WorkPlace GetWorkPlaceByDescription(string workPlaceDescription)
{
var workPlace = this.DbContext.WorkPlaces.Where(c => c.Description == workPlaceDescription).FirstOrDefault();
return workPlace;
}
}
public interface IWorkPlaceRepository : IRepository<WorkPlace>
{
WorkPlace GetWorkPlaceByDescription(string workPlaceDescription);
}
Factory
public class DbFactory : Disposable, IDbFactory
{
AgendaEntities dbContext;
public AgendaEntities Init()
{
return dbContext ?? (dbContext = new AgendaEntities());
}
protected override void DisposeCore()
{
if (dbContext != null)
dbContext.Dispose();
}
}
I tried to do something like this:
public void BasicIndexTest()
{
// Arrange
var mockRepository = new Mock<IWorkPlaceService>();
var controller = new WorkPlacesController(mockRepository.Object);
// Act
ActionResult actionResult = controller.Index() as ViewResult;
// Assert
Assert.IsInstanceOfType(actionResult, typeof(List<WorkPlace>));
}
How do I inject in this controller the data needed to go in the database and bring the results?
I Want test this public ActionResult Index() but I don't know how to Inject those dependencies.
Mock the behavior of required dependencies of the controller for the test and assert the desired behavior when the test is exercised.
For example, based on what you have done so far
public void BasicIndexTest() {
// Arrange
var mockService = new Mock<IWorkPlaceService>();
var workPlaces = new List<WorkPlace>() {
new WorkPlace()
};
mockService
.Setup(_ => _.GetWorkPlaces(It.IsAny<string>(), It.IsAny<bool>()))
.Returns(workPlaces);
var controller = new WorkPlacesController(mockService.Object);
// Act
var actionResult = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(actionResult);
var model = actionResult.Model;
Assert.IsNotNull(model)
Assert.IsInstanceOfType(model, typeof(List<WorkPlace>));
Assert.AreEqual(workPlaces, model);
}
Only the IWorkPlaceService was needed for the testing of Index action, but fake data was needed for the invocation of the GetWorkPlaces method. So the mock was configured to return a list of objects when called and pass it to the view result.
I'm a sitecore developer and I want to create a sample sitecore helix unit testing project for testing out our "HomeBottomContentController" controller:
public class HomeBottomContentController : GlassController
{
private readonly ISitecoreContext _iSitecoreContext;
public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
_iSitecoreContext = iSitecoreContext;
}
public override ActionResult Index()
{
var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
}
I have created a WTW.Feature.HomeBottomContent.Tests project, for the purpose of testing this entire component using helix unit testing. In it I have a UnitTest1.cs file with following:
namespace WTW.Feature.HomeBottomContent.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Test_ISitecoreContextInsertion()
{
var iSitecoreContext = Mock.Of<Glass.Mapper.Sc.ISitecoreContext>();
HomeBottomContentController controllerUnderTest = new HomeBottomContentController(iSitecoreContext);
var result = controllerUnderTest.Index() as ViewResult;
Assert.IsNotNull(result);
}
}
}
This test does pass, meaning "result" is NOT null; however, the problem is when I step into the Index() code, I see that the "model" variable is NULL when we do
var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
My question is, how exactly do I change this code to make sure that the "model" in that line does not become null? How do I "mock" an item in unit test code for the _iSitecoreContext so that it has a "Home_Control" template with legit values for its fields? Would that even be the right approach? Most online sources I've found do not have a similar scenario, I'm looking for the shortest code possible.
Another question I had is, how can I test the below Index() method in my [TestMethod], given that the SitecoreContext is declared inside the Index() method, rather than received in the HomeBottomContentController constructor like above? Is there a way to do that from the [TestMethod], or we have to send in the SitecoreContext into the HomeBottomContentController constructor or into the Index() method as a parameter?
public override ActionResult Index()
{
var context = new SitecoreContext();
var model = context.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
In that case you would need to mock the desired behavior on the mocked dependency
[TestClass]
public class UnitTest1 {
[TestMethod]
public void Test_ISitecoreContextInsertion() {
//Arrange
var model = new Home_Control() {
//...populate as needed
}
var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
//Setup the method to return a model when called.
iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>()).Returns(model);
var controllerUnderTest = new HomeBottomContentController(iSitecoreContext.Object);
//Act
var result = controllerUnderTest.Index() as ViewResult;
//Assert
Assert.IsNotNull(result);
Assert.IsNotNull(result.Model);
//...other assertions.
}
}
UPDATE
Creating the context within the action tightly couples it to the context, making it almost impossible to mock. That is the reason explicit dependencies are injected
You can do something like that:
public class HomeBottomContentController : GlassController
{
private readonly ISitecoreContext _iSitecoreContext;
public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
_iSitecoreContext = iSitecoreContext;
}
public override ActionResult Index()
{
var model = this.GetCurrentItem();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
protected virtual Home_Control GetCurrentItem()
{
return _iSitecoreContext.GetCurrentItem<Home_Control>();
}
}
namespace WTW.Feature.HomeBottomContent.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Test_ISitecoreContextInsertion()
{
var iSitecoreContext = Mock.Of<Glass.Mapper.Sc.ISitecoreContext>();
var controllerUnderTest = new FakeHomeBottomContentController(iSitecoreContext);
var result = controllerUnderTest.Index() as ViewResult;
Assert.IsNotNull(result);
}
}
public class FakeHomeBottomContentController : HomeBottomContentController
{
public FakeHomeBottomContentController(ISitecoreContext iSitecoreContext) : base(iSitecoreContext)
{
}
protected override Home_Control GetCurrentItem()
{
// return instance of Home_Control type
// e.g.
return new Home_Control();
}
}
}
I am running a unit test of my PostMyModel route. However, within PostMyModel() I used the line Validate<MyModel>(model) to revalidate my model after it is changed. I am using a test context, so as not to be dependent on the db for the unit tests. I have posted the test context and post method below:
Test Context
class TestAppContext : APIContextInterface
{
public DbSet<MyModel> MyModel { get; set; }
public TestAppContext()
{
this.MyModels = new TestMyModelDbSet();
}
public int SaveChanges(){
return 0;
}
public void MarkAsModified(Object item) {
}
public void Dispose() { }
}
Post Method
[Route(""), ResponseType(typeof(MyModel))]
public IHttpActionResult PostMyModel(MyModel model)
{
//Save model in DB
model.status = "Waiting";
ModelState.Clear();
Validate<MyModel>(model);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.MyModels.Add(model);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (MyModelExists(model.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DisplayMyModel", new { id = model.id }, model);
}
When the Validate<MyModel>(model) line runs, I get the error :
System.InvalidOperationException: ApiController.Configuration must not be null.
How can I correct this?
In order for the Validate command to run, there must be mock HttpRequest associated with the controller. The code to do this is below. This will mock a default HttpRequest, which is fairly unused in this case, allowing the method to be unit tested.
HttpConfiguration configuration = new HttpConfiguration();
HttpRequestMessage request = new HttpRequestMessage();
controller.Request = request;
controller.Request.Properties["MS_HttpConfiguration"] = configuration;