I have written a test class for my controller class using NUnit and Moq framework.My classes are following
public class ClientTypeController : BaseController
{
IClientTypeService clientTypeService;
IClientTypeAudService clientTypeAudService;
IClientTypeHisService clientTypeHisService;
IUserAccountService userAccountService;
System.Web.HttpResponseBase _responceBase;
public ClientTypeController()
{
}
public ClientTypeController(IClientTypeService _IClientTypeService, IClientTypeAudService _IClientTypeAudService,
IClientTypeHisService _IClientTypeHisService,IUserAccountService
_IUserAccountService)
{
clientTypeService = _IClientTypeService;
clientTypeAudService = _IClientTypeAudService;
clientTypeHisService = _IClientTypeHisService;
userAccountService = _IUserAccountService;
}
public ClientTypeController(IClientTypeAudService _clientTypeAudService,
System.Web.HttpResponseBase responceBas)
{
clientTypeAudService = _clientTypeAudService;
this._responceBase = responceBas;
}
......
.....
And my test class for the above given controller class,
public class ClientTypeControllerTest
{
private Mock<IClientTypeService> _clientTypeServiceMock;
private Mock<IClientTypeAudService> _clientTypeAudServiceMock;
private Mock<IClientTypeHisService> _clientTypeHisServiceMock;
private Mock<HttpSessionStateBase> _sessionMock;
private Mock<HttpResponseBase> _httpresponceMock;
private Mock<IUserAccountService> _userAccountService;
ClientTypeController objClientTypeController;
protected Users LoggedInUser { get; set; }
List<ClientTypeAud> lists;
[SetUp]
public void Initialize()
{
//System.Diagnostics.Debugger.Launch();
_clientTypeServiceMock = new Mock<IClientTypeService>();
_clientTypeAudServiceMock = new Mock<IClientTypeAudService>();
_clientTypeHisServiceMock = new Mock<IClientTypeHisService>();
_userAccountService = new Mock<IUserAccountService>();
_sessionMock = new Mock<HttpSessionStateBase>();
_httpresponceMock = new Mock<HttpResponseBase>();
var ctrlContext = new Mock<ControllerContext>();
AutoMapperConfiguration.Configure();
_sessionMock.SetupGet(s => s["LOGGED_IN_USER"]).Returns(users);
ctrlContext.Setup(p => p.HttpContext.Session).Returns(_sessionMock.Object);
}
[Test]
public void Show_AllClientTypeRecords_InGridView_UnitTest()
{
lists = new List<ClientTypeAud>() {
new ClientTypeAud() { Id = 1, CTypeName = "INR", CompanyId = 1, Active = "Y" },
new ClientTypeAud() { Id = 1, CTypeName = "ABC", CompanyId = 1, Active = "Y" },
new ClientTypeAud() { Id = 1, CTypeName = "AVM", CompanyId = 1, Active = "Y" }
};
_clientTypeAudServiceMock.Setup(x => x.All()).Returns(lists.AsQueryable());
objClientTypeController = new ClientTypeController(_clientTypeServiceMock.Object,
_clientTypeAudServiceMock.Object, _clientTypeHisServiceMock.Object,
_userAccountService.Object);
var result = objClientTypeController.GridData("", "asc", 1, 1) as JsonResult;
Assert.IsNotNull(result.Data);
}
}
And my BaseController class is ,
public class BaseController : Controller
{
public SessionProvider SessionProvider;
protected Users LoggedInUser { get; set; }
public string actionName { get; set; }
protected string controllerName { get; set; }
protected string area { get; set; }
public BaseController()
{
actionName =
System.Web.HttpContext.Current.Request.RequestContext.RouteData.GetRequiredString("action");
controllerName =
System.Web.HttpContext.Current.Request.RequestContext.RouteData.
GetRequiredString("controller");
SessionProvider = new SessionProvider(Session);
LoginMethod();
ViewBag.Menu = BuildMenu();
}
......
......
When I run my test class using NUnit then it shows Nullreferenceexception was unhandled by user code on
actionName = System.Web.HttpContext.Current.Request.RequestContext.
RouteData.GetRequiredString("action");
This is in my BaseControllerClass. So I don't know how to Moq the data for HttpContext.Current.Request.RequestContext.so please Can anyone please help to find the solution
You should create wrapper utility for handling the HttpContext.
This wrapper should implement an interface and in your UT you should mock that call (you can use Rhino Mocks).
Related
I am new to MVC and to unit testing so I have been following guides etc.
At the moment I am looking at unit testing. I have a test which as far as I can see should work, but unfortunately does not.
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using WorkingWithVisualStudio.Controllers.Home;
using WorkingWithVisualStudio.Models;
using Xunit;
namespace WorkingWithVisualStudio.Tests
{
public class HomeControllerTests
{
class ModelCompleteFakeRepository : IRepository
{
public IEnumerable<Product> Products { get; } = new Product[] {
new Product { Name = "P1", Price = 275M },
new Product { Name = "P2", Price = 48.95M },
new Product { Name = "P3", Price = 19.50M },
new Product { Name = "P3", Price = 34.95M }};
public void AddProduct(Product p)
{
// do nothing - not required for test
}
}
[Fact]
public void IndexActionModelIsComplete()
{
// Arrange
var controller = new HomeController();
controller.Repository = new ModelCompleteFakeRepository();
// Act
var model = (controller.Index() as ViewResult)?.ViewData.Model
as IEnumerable<Product>;
// Assert
Assert.Equal(controller.Repository.Products, model,
Comparer.Get<Product>((p1, p2) => p1.Name == p2.Name
&& p1.Price == p2.Price));
}
class ModelCompleteFakeRepositoryPricesUnder50 : IRepository
{
public IEnumerable<Product> Products { get; } = new Product[] {
new Product { Name = "P1", Price = 5M },
new Product { Name = "P2", Price = 48.95M },
new Product { Name = "P3", Price = 19.50M },
new Product { Name = "P3", Price = 34.95M }};
public void AddProduct(Product p)
{
// do nothing - not required for test
}
}
[Fact]
public void IndexActionModelIsCompletePricesUnder50()
{
// Arrange
var controller = new HomeController();
controller.Repository = new ModelCompleteFakeRepositoryPricesUnder50();
// Act
var model = (controller.Index() as ViewResult)?.ViewData.Model
as IEnumerable<Product>;
// Assert
Assert.Equal(controller.Repository.Products, model,
Comparer.Get<Product>((p1, p2) => p1.Name == p2.Name
&& p1.Price == p2.Price));
}
}
}
When I run the IndexActionModelIsCompletePricesUnder50()
I get the following:
Message: Assert.Equal() Failure
Expected: Product[] [Product { Name = "P1", Price = 5 }, Product { Name = "P2", Price = 48.95 }, Product { Name = "P3", Price = 19.50 }, Product { Name = "P3", Price = 34.95 }]
Actual: ValueCollection<String, Product> [Product { Name = "Kayak", Price = 275 }, Product { Name = "Lifejacket", Price = 48.95 }, Product { Name = "Soccer ball", Price = 19.50 }, Product { Name = "Corner flag", Price = 34.95 }]
My model is as follows:
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
My repository:
public class SimpleRepository : IRepository
{
private static SimpleRepository sharedRepository = new SimpleRepository();
private Dictionary<string, Product> products = new Dictionary<string, Product>();
public static SimpleRepository SharedRepository => sharedRepository;
public SimpleRepository()
{
var initialItems = new[]
{
new Product {Name = "Kayak", Price = 275M},
new Product { Name = "Lifejacket", Price = 48.95M },
new Product { Name = "Soccer ball", Price = 19.50M },
new Product { Name = "Corner flag", Price = 34.95M }
};
foreach(var p in initialItems)
{
AddProduct(p);
}
//products.Add("Error", null);
}
public IEnumerable<Product> Products => products.Values;
public void AddProduct(Product p) => products.Add(p.Name, p);
}
My repository interface
public interface IRepository
{
IEnumerable<Product> Products { get; }
void AddProduct(Product p);
}
My comparer:
public class Comparer
{
public static Comparer<U> Get<U>(Func<U, U, bool> func)
{
return new Comparer<U>(func);
}
}
public class Comparer<T> : Comparer, IEqualityComparer<T>
{
private Func<T, T, bool> comparisonFunction;
public Comparer(Func<T, T, bool> func)
{
comparisonFunction = func;
}
public bool Equals(T x, T y)
{
return comparisonFunction(x, y);
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
}
my controller:
public class HomeController : Controller
{
public IRepository Repository = SimpleRepository.SharedRepository;
public IActionResult Index() => View(SimpleRepository.SharedRepository.Products);
[HttpGet]
public IActionResult AddProduct() => View(new Product());
[HttpPost]
public IActionResult AddProduct(Product p)
{
Repository.AddProduct(p);
return RedirectToAction("Index");
}
}
I am sorry if this seems like a stupid question but I have only just begun to look into unit testing. If someone could explain to me what the issue is I would definitely appreciate it. Thank you very much to those who take the time to lend a hand.
I would first suggest you refactor the controller to follow a more SOLID approach by using Explicit Dependency Principle
Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.
So the controller would end up looking like this
public class HomeController : Controller {
private readonly IRepository repository;
public HomeController(IRepository repository) {
this.repository = repository;
}
public IActionResult Index() => View(repository.Products.ToList());
[HttpGet]
public IActionResult AddProduct() => View(new Product());
[HttpPost]
public IActionResult AddProduct(Product p) {
repository.AddProduct(p);
return RedirectToAction("Index");
}
}
So as to avoid the mistake initially made with accessing the share repository during an isolated unit test, which caused your assertions to fail.
Try to avoid tightly coupling your classes to static or shared dependencies. It would be safer to inject the abstraction of that dependency.
A simplified version of the test can now be clearly exercised as follows.
class ModelCompleteFakeRepository : IRepository {
public IEnumerable<Product> Products { get; } = new Product[] {
new Product { Name = "P1", Price = 275M },
new Product { Name = "P2", Price = 48.95M },
new Product { Name = "P3", Price = 19.50M },
new Product { Name = "P3", Price = 34.95M }
};
public void AddProduct(Product p) {
// do nothing - not required for test
}
}
[Fact]
public void IndexActionModelIsComplete() {
// Arrange
var repository = new ModelCompleteFakeRepository();
var controller = new HomeController(repository);
var expected = repository.Products;
// Act
var actual = (controller.Index() as ViewResult)?.ViewData.Model as IEnumerable<Product>;
// Assert
Assert.IsNotNull(actual);
Assert.Equal(expected, actual);
}
Because in your Index method you're referring to the SimpleRepository, not you Repository member.
Replace
public IActionResult Index() => View(SimpleRepository.SharedRepository.Products);
with
public IActionResult Index() => View(Repository.Products);
I should add as well that you might want to have a look at the structure of your code, and inject the repository in the constructor instead. And also, the two different tests you have test the same thing, so only one of them is necessary.
Edit: My answers solves your current issue, while #Nkosi answer shows how you should do this properly.
all
I was create IDbCommandTreeInterceptor and got the problem: EF provider wrong sql generation. As a result, I want to get this SQL
UPDATE [dbo].[Devices] SET [DeletedDate] = CASE
WHEN [DeletedDate] IS NULL THEN GETUTCDATE()
ELSE [DeletedDate] END
Code for testing. Interseptor class for fake delettion.
public class SoftDeleteInterseptor : IDbCommandTreeInterceptor
{
private const string DELETED_DATE_COLUMN = "DeletedDate";
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace)
{
return;
}
var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree;
if (deleteCommand != null)
{
interceptionContext.Result = HandleDeleteCommand(deleteCommand);
return;
}
}
private DbCommandTree HandleDeleteCommand(DbDeleteCommandTree deleteCommand)
{
if (!IsPropertyExists(deleteCommand, DELETED_DATE_COLUMN))
{
return deleteCommand;
}
var deletedProperty = DbExpressionBuilder.Property(
DbExpressionBuilder.Variable(deleteCommand.Target.VariableType, deleteCommand.Target.VariableName),
DELETED_DATE_COLUMN
);
var caseValue = DbExpressionBuilder.Case(
new DbExpression[] { deletedProperty.IsNull() },
new DbExpression[] { EdmFunctions.CurrentUtcDateTime() },
deletedProperty);
var setClauses = new List<DbModificationClause> { DbExpressionBuilder.SetClause(deletedProperty, caseValue) };
return new DbUpdateCommandTree(
deleteCommand.MetadataWorkspace,
deleteCommand.DataSpace,
deleteCommand.Target,
deleteCommand.Predicate,
setClauses.AsReadOnly(), null);
}
private bool IsPropertyExists(DbModificationCommandTree command, string property)
{
var table = (EntityType)command.Target.VariableType.EdmType;
return table.Properties.Any(p => p.Name == property);
}
}
Create configuration class for register DbInterseptor.
public class CustomDbConfiguration : DbConfiguration
{
public CustomDbConfiguration()
{
AddInterceptor(new SoftDeleteInterseptor());
}
}
public partial class CustomDbContext : DbContext
{
static IDCompleteDbContext()
{
DbConfiguration.SetConfiguration(new CustomDbConfiguration());
}
public virtual DbSet<CommandEntity> CommandEntities { get; set; }
}
public class CommandEntity
{
public int Id {get; set;}
public DateTime? DeletedDate {get; set;}
public string Name {get; set;}
}
When delete entity, entity did not deleted
var context = new CustomDbContext();
var entity = context.CommandEntities.First();
context.CommandEntities.Remove(entity);
context.SubmitChanges();
Did not work. EF provider generate wrong SQL: UPDATE [dbo].[Devices] SET [DeletedDate] = [DeletedDate] IS NULL#0[DeletedDate] WHERE ([Id] = #1) #0: '01.05.2018 7:45:22' (Type = DateTime2) #1: '20' (Type = Int32)
I created a sample ASP.NET MVC application.The default application provided by the Visual Studio.
I created folder named Utilities and place the below 4 classes
Connection
ConnectionRepository
SecurityManager
SessionManger
Below are the code of the above classes
public class Connection
{
public string ConnectionString { get; set; }
public Connection(string connectionStr)
{
this.ConnectionString = connectionStr;
}
public User GetUser(string userId)
{
return new User() { UserId="User1",Email="User1#company.com" };
}
}
******
using LegacyHttpContext = System.Web.HttpContext;
namespace SampleNunitApplication.Utilities
{
public class ConnectionRepository
{
public Connection ConnectionData
{
get
{
Connection con;
if(Convert.ToBoolean(LegacyHttpContext.Current.Application["isSourceOffline"].ToString()))
{
con = this.RefreshConfigDataFromAppServer();
}
else
{
con = this.RefreshConfigDataFromDB();
}
return con;
}
}
public Connection RefreshConfigDataFromDB()
{
Connection dbConnection = new Utilities.Connection("DataFromSqlServer");
LegacyHttpContext.Current.Application["_connData"] = dbConnection;
return dbConnection;
}
public Connection RefreshConfigDataFromAppServer()
{
Connection appConnection = new Utilities.Connection("DataFromAppServer");
LegacyHttpContext.Current.Application["_connData"] = appConnection;
return appConnection;
}
}
}
*******
using LegacyHttpContext = System.Web.HttpContext;
namespace SampleNunitApplication.Utilities
{
public class SecurityManager
{
public static SecurityManager GetFromCache()
{
if(LegacyHttpContext.Current.Application["SecurityManager"]==null)
{
SecurityManager securityManager = new SecurityManager();
LegacyHttpContext.Current.Application["SecurityManager"] = securityManager;
return securityManager;
}
else
{
return LegacyHttpContext.Current.Application["SecurityManager"] as SecurityManager;
}
}
public User GetAuthenticatedUser(string userId)
{
ConnectionRepository connRepository = new ConnectionRepository();
Connection con = connRepository.ConnectionData;
return con.GetUser(userId);
}
}
}
*****
public class SessionManager
{
public string EmployeeId { get; set; }
public string Email { get; set; }
public static void SaveToSessionState(HttpSessionStateBase state,SessionManager sessionManager)
{
state["SessionManager"] = sessionManager;
}
public static SessionManager LoadFromSessionState(HttpSessionStateBase state)
{
if (state["SessionManager"] ==null)
{
return new SessionManager();
}
return state["SessionManager"] as SessionManager;
}
}
****In the HomeController.cs***
public ActionResult Index()
{
SessionManager authenticateUser = SessionManager.LoadFromSessionState(this.HttpContext.Session);
ValidationModel.Validate(authenticateUser.EmployeeId, this.HttpContext);
SessionManager.LoadFromSessionState(this.HttpContext.Session);
return View();
}
*******************In the Global.asax***
protected void Session_Start()
{
HttpSessionStateWrapper sessionWrapper = new HttpSessionStateWrapper(System.Web.HttpContext.Current.Session);
System.Web.HttpContext.Current.Application["isSourceOffline"] = false;
SessionManager authenticateUser = SessionManager.LoadFromSessionState(sessionWrapper);
User user = SecurityManager.GetFromCache().GetAuthenticatedUser("User1");
authenticateUser.EmployeeId = user.UserId;
authenticateUser.Email = user.Email;
SessionManager.SaveToSessionState(sessionWrapper, authenticateUser);
}
******
I created a class library project and created below mock code
[TestClass] public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var application = new Mock<HttpApplicationStateBase>();
SecurityManager securityManager = new SecurityManager();
application.SetupGet(s => s["isSourceOffline"]).Returns(true);
application.SetupGet(s => s["SecurityManager"]).Returns(securityManager);
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>();
session.Setup(s => s.SessionID).Returns(Guid.NewGuid().ToString());
context.Setup(c => c.Request).Returns(request.Object);
context.Setup(c => c.Response).Returns(response.Object);
context.Setup(c => c.Session).Returns(session.Object);
context.Setup(c => c.Application).Returns(application.Object);
context.Setup(c => c.Server).Returns(server.Object);
SessionManager authenticatedUser = new SessionManager();
authenticatedUser.EmployeeId = "TestUser";
authenticatedUser.Email = "Test#testmail.com";
SessionManager.SaveToSessionState(context.Object.Session, authenticatedUser);
HomeController objController = new HomeController();
objController.ControllerContext = new System.Web.Mvc.ControllerContext(context.Object, new RouteData(), objController);
objController.Index();
}
}
If I run the ASP.NET MVC application it works good. But when i run the test case it hits the Index method of the HomeController but SessionManager authenticateUser = SessionManager.LoadFromSessionState(this.HttpContext.Session); is not populating from the session. Properties in the authenticateUser are null.
Seems like mock session is not working please help
So I discovered an interesting problem.
I have a model like this:
public class ApplicantModel
{
[Display(Name = "Firstname", ResourceType = typeof(Resources))]
[MaxLength(50, ErrorMessageResourceName = "FirstName", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
[Required(ErrorMessageResourceName = "FirstName", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
public string Firstname { get; set; }
[Display(Name = "Surname", ResourceType = typeof(Resources))]
[MaxLength(50, ErrorMessageResourceName = "Surname", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
[Required(ErrorMessageResourceName = "Surname", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
public string Surname { get; set; }
}
that is all fine, and when I check the Model state and there is an error on a model I get something like this:
errors:
[{
Key = FirstApplicant.Firstname
Value = ["First name is required field"]
},
{
Key = FirstApplicant.Surname
Value = ["Surname name is required field"]
}].
That is also fine.
Edit:
This is the c# ModelState object visualized as JSON object. Real object looks like this:
ModelState
{System.Web.Mvc.ModelStateDictionary}
Count: 2
IsReadOnly: false
IsValid: false
Keys: Count = 2
Values: Count = 2
Results View: Expanding the Results View will enumerate the IEnumerable
However my question is. Is it possible to somehow change the key? I know that the key is created as the name of object and then the name property on that object.
So it makes sense, but is there any way how to change this default behavior? Or do I have to change the names of objects?
Edit2:
What I am trying to achieve here is that I have a c# ViewModel and knockout ViewModel. and when you do server side validations you get this dictionary of keys and values which I serialize and send to client.
And then I call this function on it on client:
var errors = #Html.Raw(Json.Encode(Model.Errors));
function showErrors(serializedErrors) {
var errors = JSON.parse(serializedErrors);
for (var i = 0; i < errors.length; i++) {
var error = errors[i];
var key = error.Key;
var property = eval("masterModel." + key);
property.setError(error.Value.ErrorMessage);
property.isModified(true);
}
}
showErrors(errors);
And this would work fine if the view model property names match on the server and on client. But for example on server side I have a FirstApplicant.FirstName and on a client side it is ApplicantOne.firstname. Thank you all for help and comments. I hope I explained my problem in more detail this time.
in the end I found a solution to this problem. It is a bit complicated but it works.
First I've created an attribute.
public class ClientNameAttribute : Attribute, IMetadataAware
{
public ClientNameAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["ClientName"] = this.Name;
}
}
Notice that this attribute also implements IMetadataAware
Next step was to create Html helper, so I could call this in a view.
public static class HtmlHelperExtensions
{
public static string CustomModelState<T>(this HtmlHelper<T> helper)
{
var errors = helper.ViewData.ModelState.Select(
m => new { Key = GenerateClientName(m.Key, helper), Value = m.Value.Errors.FirstOrDefault() }).Where(e=> e.Value != null);
return Json.Encode(errors);
}
private static string GenerateClientName<T>(string key, HtmlHelper<T> helper)
{
StringBuilder builder = new StringBuilder();
int periodIndex = -1;
do
{
periodIndex = key.IndexOf('.', periodIndex + 1);
string part = key.Substring(0, periodIndex==-1 ? key.Length : periodIndex);
var partMetadata = ModelMetadata.FromStringExpression(part, helper.ViewData);
object clientName;
if (builder.Length > 0)
{
builder.Append('.');
}
if (partMetadata.AdditionalValues.TryGetValue("ClientName", out clientName))
{
builder.Append(clientName);
}
else
{
builder.Append(partMetadata.PropertyName);
}
}
while (periodIndex != -1);
return builder.ToString();
}
}
CustomModelState is a method that I call in a view.
like this:
var errors = #Html.Raw(Html.CustomModelState());
if (errors.length > 0) {
showErrors("masterModel",errors);
}
this will give you nicely formated errors, with your custom names of properties.
And here are tests for it:
public class TestModel
{
[Required]
public string Normal { get; set; }
[ClientName("Other")]
[Required]
public string Changed { get; set; }
[ClientName("Complicated")]
public TestModelTwo TestModelTwo { get; set; }
}
public class TestModelTwo
{
public string PropertyOne { get; set; }
[ClientName("Two")]
public string PropertyTwo{ get; set; }
}
[TestClass]
public class HtmlHelperExtensionsTests
{
[TestMethod]
public void CustomModelStateTests()
{
var model = new TestModel();
var page = new ViewPage();
page.ViewData.Model = model;
page.ViewData.ModelState.AddModelError("Normal", "Error1");
page.ViewData.ModelState.AddModelError("Changed", "Error2");
HtmlHelper<TestModel> helper = new HtmlHelper<TestModel>(new ViewContext(), page);
var custom = helper.CustomModelState();
string expectedResult =
"[{\"Key\":\"Normal\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error1\"}},{\"Key\":\"Other\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error2\"}}]";
Assert.AreEqual(expectedResult, custom);
}
[TestMethod]
public void CustomModelStateTests_ObjectProperty_With_ClientName()
{
var model = new TestModel();
model.TestModelTwo = new TestModelTwo();
var page = new ViewPage();
page.ViewData.Model = model;
page.ViewData.ModelState.AddModelError("TestModelTwo.PropertyOne", "Error1");
page.ViewData.ModelState.AddModelError("TestModelTwo.PropertyTwo", "Error2");
HtmlHelper<TestModel> helper = new HtmlHelper<TestModel>(new ViewContext(), page);
var custom = helper.CustomModelState();
string expectedResult =
"[{\"Key\":\"Complicated.PropertyOne\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error1\"}},{\"Key\":\"Complicated.Two\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error2\"}}]";
Assert.AreEqual(expectedResult, custom);
}
}
I currently use the following approach to create a strongly typed object representing session variables.
public abstract class SessionController : Controller
{
private const string SESSION_NAME = "UserSession";
public SessionData SessionData
{
get
{
SessionData sessionData = (SessionData)Session[SESSION_NAME];
if (sessionData != null)
{
return sessionData;
}
else
{
sessionData = new SessionData();
Session[SESSION_NAME] = sessionData;
return sessionData;
}
}
set
{
Session[SESSION_NAME] = value;
}
}
}
SessionData is a simple object like for example
[Serializable]
public class SessionData
{
public String SessionId { get; set; }
public Int64 UserId { get; set; }
public String NameOfUser { get; set; }
}
When creating a new Controller I derivate it from the SessionController so that I have strongley typed access to my SessionData. For example
public CityController : SessionController
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
ViewData.Model = _cityService.GetAll(SessionData.UserId);
return View("Index");
}
}
So, I am struggling at the moment to get this approached covered by a unittest. A shortened version of what I have tried is the following snippet
[SetUp]
public void SetUp()
{
mocks = new MockRepository();
_cityService = MockRepository.GenerateStub<ICityService>();
_sesssionData = new SessionData { UserId = 1, SessionId = "1" };
// First Approach
controller = new CityController(_cityService);
controller.Expect(p => p.SessionData).Return(_sesssionData);
// Second Approach
cctx = MockRepository.GenerateStub<ControllerContext>();
cctx.Expect(p=>p.HttpContext.Session["UserSession"] as SessionData).Return(_sesssionData);
controller.ControllerContext = cctx;
}
Has anyone a tip on how to get this problem solved?
If you make your SessionData property virtual then your first approach could work:
// Arrange
var mocks = new MockRepository();
var cityService = MockRepository.GenerateStub<ICityService>();
var sesssionData = new SessionData { UserId = 1, SessionId = "1" };
var controller = mocks.PartialMock<CityController>(cityService);
controller.Expect(c => c.SessionData).Return(sessionData);
controller.Replay();
// Act
controller.Index();
//Assert
...
IMO this approach is not very good because the SUT (Subject Under Test => in this case CityController) is being mocked with PartialMock.