Mocking HttpContext to retrieve item form contexts item dictionary - asp.net-mvc-3

We use MVC3, for our unit tests we use RhinoMocks in our unit tests.
When the a request starts we check the domain from which it came and match that to a customer.
This customer is stored in the HttpContext.Items.
Most controllers need this info to do their thing.
var mocks = new MockRepository();
using (var controller = new TestController())
{
HttpContext context =
MockRepository.GenerateStub<HttpContext>();
Customer customer = new Customer { Key = "testKey" };
context.Items["Customer"] = customer;
controller.ControllerContext =
new ControllerContext {
Controller = controller,
RequestContext =
new RequestContext(
new HttpContextWrapper(context),
new RouteData()
)
};
...
This code sample shows basically what is needed, however the stub is not allowed as HttpContext is a "sealed" class.
The controller accepts a HttpContextBase (there is lot about mocking this one), but it does not expose the Items property.
Thoughts anyone? Or even better a solution ;-)

Creating a HttpContextBase stub and stubbing its Items property will allow you to use the Items dictionary:
HttpContextBase context =
MockRepository.GenerateStub<HttpContextBase>();
Customer customer = new Customer { Key = "testKey" };
context.Stub(c => c.Items).Return(new Dictionary<string, object>());
context.Items["Customer"] = customer;

Related

Update: Passing HttpContext and IPrincipal to Controler in ReSharper unit test

I'm attempting to run a couple basic unit tests on an ASP MVC controller, however at one point the controller needs to examine the IPrincipal User object like so:
ViewBag.Level = Security.GetLevel(User);
Once the unit test enters the Create controller method, however, the above line throws a NullReferenceException as the User object is null.
Any ideas on how to set an IPrincipal for a unit test session?
Here's the test as I have it written right now. I attempted to access the User object and simply set it before the test goes into the Create method, however the intellisense isn't picking it up.
[Test]
public void a06_CloneFromDatabase()
{
using (AgentResources db = new AgentResources())
{
var master = (from a in db.AgentTransmission
where a.RecordStatus.Equals("C")
select a).FirstOrDefault();
var result = _controlAgtTran.Create(master.ID, null, string.Empty, string.Empty, string.Empty, string.Empty, false) as ViewResult;
var model = result.Model as AgentTransmission;
Assert.AreEqual(model.ReferenceType, master.ReferenceType);
}
}
EDIT
Following the post mentioned in the comments below I found a method to create a HttpContext session and apply and IPrincipal to that session. This works fine until the unit test moves into the controller where the HttpContext and IPrincipal User objects are all, once again, null.
Since it seems the instance of the controller I'm using has it's HttpContext property as read only (and the IPrincipal User property as well) does anyone know of a way to pass the HttpContext being used in the unit test inside the controller being tested? Also, if this is not possible, what is a usable method for testing RouteValues using ReSharper's unit tests?
[SetUp]
public void SetUp()
{
_controlAgtTran = new AgentTransmissionController();
/****Set up Current HTTP Context to pass Security.cs checks*****/
//Set up the HTTP Request
var httpRequest = new HttpRequest("", "http://localhost:2574/", "");
//Set up the HTTP Response
var httpResponse = new HttpResponse(new StringWriter());
//Set up the HTTP Context
var httpContext = new HttpContext(httpRequest, httpResponse);
var sessionContainer = new HttpSessionStateContainer("NEAROD",
new SessionStateItemCollection(),
new HttpStaticObjectsCollection(),
100,
true,
HttpCookieMode.AutoDetect,
SessionStateMode.InProc,
false);
httpContext.Items["AspSession"] =
typeof (HttpSessionState)
.GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Standard,
new[] {typeof (HttpSessionStateContainer)},
null)
.Invoke(new object[] {sessionContainer});
//Assign the context
HttpContext.Current = httpContext;
}
[Test]
public void a01_IncompleteRecordGoesToEdit()
{
AgentTransmission agtTran = new AgentTransmission();
agtTran.ReferenceNumber = 95820518787;
agtTran.ReferenceType = "S";
agtTran.EffectiveDate = DateTime.Now;
agtTran.RelationshipEffDate = DateTime.Now;
agtTran.RecordStatus = "N";
agtTran.CreatedDate = DateTime.Now;
agtTran.CreatedOperator = "xTest1";
agtTran.FirstName = "Unit";
agtTran.LastName = "Test";
agtTran.ExtRepType = "EXTREPID";
agtTran.JIT = true;
agtTran.SendToDRM = true;
agtTran.SendToNMF = true;
agtTran.WelcomeLetter = true;
agtTran.OverrideRegionInd = false;
//set IPrincipal
string[] roles = {"LCO"};
IPrincipal principal = new GenericPrincipal(new GenericIdentity("SYMETRA\\NEAROD"), roles);
HttpContext.Current.User = principal;
IPrincipal user = HttpContext.Current.User;
Assert.AreEqual(user, principal); //This passes
Assert.AreEqual(principal, _controlAgtTran.User); //this fails
var result = (RedirectToRouteResult)_controlAgtTran.Create(agtTran); //this crashes
//Tests aren't run
Assert.IsNotNull(result);
Assert.AreEqual(3, result.RouteValues.Count);
Assert.AreEqual("AgentTransmission", result.RouteValues["controller"]);
Assert.AreEqual("Edit", result.RouteValues["action"]);
}
Following a similar solution mentioned in this post I added the following to the end of the SetUp() method.
var controllerCtx = new ControllerContext();
controllerCtx.HttpContext = new HttpContextWrapper(HttpContext.Current);
_controlAgtTran.ControllerContext = controllerCtx;
Wrapping the current HttpContext inside an HttpContextBase property (the inappropriately named controllerCtx.HttpContext) the test now has access to the User and HttpContext properties of the controller. These properties were previously read-only when using just the HttpContext.Current session and therefore always null.
FYI - this is my first time unit testing with these objects so that explanation may be less than 100% correct. Please feel free to comment below and I'll make any necessary changes.

Instantiate new System.Web.Http.OData.Query.ODataQueryOptions in nunit test of ASP.NET Web API controller

I have an ASP.NET MVC4 Web API project with an ApiController-inheriting controller that accepts an ODataQueryOptions parameter as one of its inputs.
I am using NUnit and Moq to test the project, which allow me to setup canned responses from the relevant repository methods used by the ApiController. This works, as in:
[TestFixture]
public class ProjectControllerTests
{
[Test]
public async Task GetById()
{
var repo = new Mock<IManagementQuery>();
repo.Setup(a => a.GetProjectById(2)).Returns(Task.FromResult<Project>(new Project()
{
ProjectID = 2, ProjectName = "Test project", ProjectClient = 3
}));
var controller = new ProjectController(repo.Object);
var response = await controller.Get(2);
Assert.AreEqual(response.id, 2);
Assert.AreEqual(response.name, "Test project");
Assert.AreEqual(response.clientId, 3);
}
}
The challenge I have is that, to use this pattern, I need to pass in the relevant querystring parameters to the controller as well as the repository (this was actually my intent). However, in the case of ODataQueryOptions-accepting ApiController methods, even in the cases where I would like to use just the default parameters for ODataQueryOptions, I need to know how to instantiate one. This gets tricky:
ODataQueryOptions does not implement an interface, so I can't mock it directly.
The constructor requires an implementation of System.Web.Http.OData.ODataQueryContext, which requires an implementation of something implementing Microsoft.Data.Edm.IEdmModel, for which the documentation is scarce and Visual Studio 2012 Find References and View Call Hierarchy do not provide insight (what implements that interface?).
What do I need to do/Is there a better way of doing this?
Thanks.
Looks like someone else already answered this in the comments here, but it's not a complete solution for my use-case (see comment below):
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Customer>("Customers");
var opts = new ODataQueryOptions<Customer>(new ODataQueryContext(modelBuilder.GetEdmModel(),typeof(Customer)), request);
This is the solution I have been using in my NUnit tests to inject ODataQueryOptions
private static IEdmModel _model;
private static IEdmModel Model
{
get
{
if (_model == null)
{
var builder = new ODataConventionModelBuilder();
var baseType = typeof(MyDbContext);
var sets = baseType.GetProperties().Where(c => c.PropertyType.IsGenericType && c.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>));
var entitySetMethod = builder.GetType().GetMethod("EntitySet");
foreach (var set in sets)
{
var genericMethod = entitySetMethod.MakeGenericMethod(set.PropertyType.GetGenericArguments());
genericMethod.Invoke(builder, new object[] { set.Name });
}
_model = builder.GetEdmModel();
}
return _model;
}
}
public static ODataQueryOptions<T> QueryOptions<T>(string query = null)
{
query = query ?? "";
var url = "http://localhost/Test?" + query;
var request = new HttpRequestMessage(HttpMethod.Get, url);
return new ODataQueryOptions<T>(new ODataQueryContext(Model, typeof(T)), request);
}

How to check attribute of action from HttpRequest

I've followed this Prevent Forms authentication in order to try and handle redirecting from ajax gracefully. However I need to be able to determine if certain attributes are decorating the action that this call was made for as I only want to do this for some occasions. Can I get this information from the HttpRequest object that is accessible within this method?.
Essentially taking the part from the code above that I would like to manipulate:
public class SuppressFormsAuthenticationRedirectModule : IHttpModule {
private void OnPostReleaseRequestState(object source, EventArgs args) {
var context = (HttpApplication)source;
var response = context.Response;
var request = context.Request; // request is HttpRequest
if (response.StatusCode == 401 && request.Headers["X-Requested-With"] ==
"XMLHttpRequest") {
// TODO HERE: Check that the controller action contains a particular attribute
// and if so do not suppress redirect
SuppressAuthenticationRedirect(context.Context);
}
}
}
UPDATE:
It's probably worth noting that this code is held within a compiled DLL project that is then encorporated into a host MVC application (which we don't have access to). In that case I don't really have access to changing default implementations unless I can ensure it doesn't effect the rest of the controllers in the application.
I tried to use as much of the framework as possible, which is why I chose to expose the GetControllerType method from the DefaultControllerFactory. You'll notice that routeData contains the area, controller and action, so with a bit of reflection, you can bypass having to create a derived controller factory.
This is definitely not production ready. It is just a way to get the custom attributes from the requested action.
Edit: instead of setting the current controller factory, create a new DerivedControllerFactory
var httpApplication = (HttpApplication)sender;
var httpContext = new HttpContext(httpApplication.Request, new HttpResponse(new StringWriter()));
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
//var factory = ControllerBuilder.Current.GetControllerFactory() as DerivedControllerFactory;
var factory = new DerivedControllerFactory();
var controllerType = factory.GetControllerType(new RequestContext(new HttpContextWrapper(httpContext), routeData), routeData.Values["controller"].ToString());
var methodInfo = controllerType.GetMethod(routeData.Values["action"].ToString());
var attributes = methodInfo.GetCustomAttributes(true);
public class DerivedControllerFactory : DefaultControllerFactory
{
public new Type GetControllerType(RequestContext requestContext, string controllerName)
{
return base.GetControllerType(requestContext, controllerName);
}
}

Copy a note from a task to a case

I have a manually invoked process which is tied to the account entity. This process has a number of steps. One of the first steps is to create a task and assign it to someone. It's expected that this person will add some notes and complete the task.
Further down the process, I have a step to create a service case. After this is created, I want to copy the note(s) from the task above to the newly created service case.
I have created a custom workflow activity to try and accomplish this. I have gotten as far as deploying it and using it within my process without any errors and it does copy content into the notes field of the service case, however it copies the title of the task, not the note content and I can't quite fathom out why.
public class CopyNotes : CodeActivity
{
protected override void Execute(CodeActivityContext executionContext)
{
//Create the context
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
//get the notes associated with the source entity
Guid copyFromId = CopyFrom.Get(executionContext).Id;
Guid copyToId = CopyTo.Get(executionContext).Id;
EntityCollection copyFromNotes = RetrieveNotes(service, copyFromId);
if (copyFromNotes.Entities.Any())
{
foreach (Entity e in copyFromNotes.Entities)
{
Entity newNote = new Entity("annotation");
newNote.Attributes["subject"] = e.Attributes["subject"];
newNote.Attributes["notetext"] = e.Attributes["notetext"];
newNote.Attributes["objectid"] = new EntityReference() { Id = copyToId, LogicalName = CopyTo.Get(executionContext).LogicalName };
}
}
}
private EntityCollection RetrieveNotes(IOrganizationService service, Guid relatedObject)
{
ConditionExpression condition = new ConditionExpression();
condition.AttributeName = "objectid";
condition.Operator = ConditionOperator.Equal;
condition.Values.Add(relatedObject.ToString());
ColumnSet columns = new ColumnSet("subject", "notetext");
QueryExpression query = new QueryExpression();
query.ColumnSet = columns;
query.EntityName = "annotation";
query.Criteria.AddCondition(condition);
EntityCollection results = service.RetrieveMultiple(query);
return results;
}
[RequiredArgument]
[ReferenceTarget("task")]
[Input("Copy notes from item")]
public InArgument<EntityReference> CopyFrom { get; set; }
[RequiredArgument]
[ReferenceTarget("incident")]
[Input("Copy notes to item")]
public InArgument<EntityReference> CopyTo { get; set; }
}
I think you need to actually create the newNote after defining it.
foreach (Entity e in copyFromNotes.Entities)
{
Entity newNote = new Entity("annotation");
newNote.Attributes["subject"] = e.Attributes["subject"];
newNote.Attributes["notetext"] = e.Attributes["notetext"];
newNote.Attributes["objectid"] = new EntityReference() { Id = copyToId, LogicalName = CopyTo.Get(executionContext).LogicalName };
service.Create(newNote);
}
Once I did that your code worked just fine creating a new note with both the title and note text.
You could do this with a standard workflow, if Async creation is fast enough.
Andreas

Testing repository method

As the title suggests I'm trying to test a method, unfortunately I appear to be going wrong some where. The method should only return customers that have and ID = 1
Here is my test
[TestMethod]
public void Returns_Correct_Number_Of_Workout_Dates_For_Valid_UserId()
{
//Arrange
List<GymSession> gymSessions = new List<GymSession>();
Customer cust = new Customer();
cust.CustomerId = 1;
gymSessions.Add(new GymSession() { Customer = cust, SessionId = 1, Date = new DateTime(2010, 1, 1) });
gymSessions.Add(new GymSession() { Customer = cust, SessionId = 2, Date = new DateTime(2010, 1, 2) });
gymSessions.Add(new GymSession() { SessionId = 3, Date = new DateTime(2010, 1, 3) });
gymSessions.Add(new GymSession() { Customer = cust, SessionId = 4, Date = new DateTime(2010, 1, 4) });
var mockRepos = new Moq.Mock<IGymSessionRepository>();
mockRepos.Setup(g => g.GymSession()).Returns(gymSessions.AsQueryable());
//Act
var result = mockRepos.Object.GetWorkoutDatesByCustomerId(1);
//Assert
Assert.AreEqual(3, result.Count());
}
Here is the repository method I'm trying to test
public IQueryable<GymSession> GetWorkoutDatesByCustomerId(int userId)
{
var gymSess = db.GymSessions.Where<GymSession>(g => g.Customer.CustomerId == userId);
return gymSess;
}
The idea is that setup has all the customers, and the method then filters them. The count never seems to apply the filter. Any ideas?
It appears that you really want to stub the call to db.GymSessions and that you should test a concrete GymSessionRepository instance. Traditionally, there are two ways to do this (apart from intercepting calls using aspect-oriented programming):
1) Give your repository an explicit dependency on db and require it in the repository constructor. Here's what I mean, where I'm using IDatabase to represent db:
public class GymSessionRepository: IGymSessionRepository {
private IDatabase db;
public GymSessionRepository(IDatabase db) {
this.db = db;
}
}
// Then in your test ...
var mockDb = new Moq.Mock<IDatabase>();
mockDb.Setup(d => d.GymSessions()).Returns(gymSessions.AsQueryable());
GymSessionRepository repository = new GymSessionRepository(mockDb);
// ... and so on
2) (Less desirable, but sometimes necessary) Expose the method you want to stub as a virtual member, mock the concrete object you're testing, and stub the behavior directly on the class under test:
public class GymSessionRepository {
// Because this is virtual, you can override it in your mock object
protected virtual List<GymSession> GymSessions() {
return this.db.GymSessions.AsQueryable();
}
}
// In your test code here: notice the mock object is your concrete class,
// because your test targets another method on that class, not a method
// on an arbitrary implementation (like a mock based on its interface)
var mockRepos = new Moq.Mock<GymSessionRepository>();
// Override its virtual method; take control for your test
mockRepos.Setup(g => g.GymSessions()).Returns(gymSessions.AsQueryable());
Depending on the mocking framework, the second technique is known as using a transparent or partial mock. If you find yourself using it often, it may be a sign that your code is overly-coupled (and it can get confusing fast).

Resources