Instantiate new System.Web.Http.OData.Query.ODataQueryOptions in nunit test of ASP.NET Web API controller - asp.net-web-api

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);
}

Related

Register action on activation for every ApiController

I'm doing tests for some of my controller's actions in WebAPI. I'm trying to configure Autofac so it will be one universal scope with settings for every test.
Everytime an ApiController is requested from Autofac I want to for instance create a ClaimsIdentity so the user authentication can be tested.
This is how I try to do it:
newBuilder.RegisterType<ApiController>().OnActivated(c =>
{
var controller = c.Instance;
controller.Request = Message;
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, UserId));
controller.User = new ClaimsPrincipal(identity);
});
newBuilder.Update(container);
However this doesn't work. If I replace ApiController with any actual controller then it works.
My controllers are registered like this:
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
I've actually managed to do something like I described, although I'm not perfectly satisfied with the solution (but it works).
// Get any controller from the namespace where all the controllers are
var asm = Assembly.GetAssembly(typeof(UserController));
foreach (var type in asm.GetTypes())
{
if (type.Namespace == "Namespace.With.Controllers" && type.IsSubclassOf(typeof(ApiController)))
{
newBuilder.RegisterType(type).OnActivated(c =>
{
var controller = (ApiController)c.Instance;
controller.Request = Message;
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, UserId));
controller.User = new ClaimsPrincipal(identity);
});
}
}

How do you read POST data in an ASP.Net MVC 3 Web API 2.1 controller?

This does not seem to be as easy as I thought. I found some solutions on the web, but they are not working for me. I have an ASP.Net MVC 3 project with the Microsoft ASP.Net Web API 2.1 nuget package installed. Now, I want to be able to read data posted to a web api controller. The data sent will vary, so I cannot used a strongly typed ViewModel.
Here are the solutions I tried:
public void Post([FromBody]string value)
{
...
}
public void Post([FromBody]List<string> values)
{
...
}
public void Post([FromBody]NameValueCollection values)
{
...
}
But my value or values variables are always empty. I know the controller is receiving data however because I can check it by accessing (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"].Request.Form. It does not look like the proper way to retrieve the data though. There ought to be a cleaner way.
UPDATE:
Here is how I am posting the information:
I am posting the data from another controller in the same web application:
public ActionResult SendEmailUsingService()
{
dynamic email = new ExpandoObject();
email.ViewName = "EmailTest";
email.From = "fromaddress#yahoo.com";
email.To = "toaddress#gmail.com";
email.Fullname = "John Smith";
email.Url = "www.mysite.com";
IDictionary<string, object> data = email;
using (var wb = new WebClient())
{
string url = BaseUrlNoTrailingSlash + Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Emailer" });
var response = wb.UploadValues(url, "POST", data.ToNameValueCollection());
}
return View();
}
And here is what I am getting in my Post web api controller if I declare an httpContext variable like this:
var httpContext = (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"];
httpContext.Request.Form =
{ViewName=EmailTest&From=fromaddress%40yahoo.com&To=toaddress%40gmail.com&Fullname=John+Smith&Url=www.mysite.com}
httpContext.Request.Form is a System.Collections.Specialized.NameValueCollection {System.Web.HttpValueCollection}
I finally found the answer to my question here:
Web API Form Data Collection
The solution is to use FormDataCollection:
public void Post([FromBody]FormDataCollection formData)
{
...
}

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);
}
}

how to unit test controller when automapper is used?

here's my controller
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = Mapper.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
here's my unit test:
[Test]
public void Signup_Action_When_The_User_Model_Is_Valid_Returns_RedirectToRouteResult()
{
// Arrange
const string expectedRouteName = "~/Views/Home/Index.cshtml";
var registeredUser = new UserRegisterViewModel { Email = "newuser#test.com", Password = "123456789".Hash()};
var confirmation = new ActionConfirmation<User>
{
WasSuccessful = true,
Message = "",
Value = new User()
};
_userService.Setup(r => r.AddUser(new User())).Returns(confirmation);
_accountController = new AccountController(_userService.Object);
// Act
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result, "Should have returned a RedirectToRouteResult");
Assert.AreEqual(expectedRouteName, result.RouteName, "Route name should be {0}", expectedRouteName);
}
Unit test failed right here.
var result = _accountController.Signup(registeredUser) as RedirectToRouteResult;
when I debug my unit test, I got following error message: "Missing type map configuration or unsupported mapping."
I think its because configuration is in web project, not the unit test project. what should I do to fix it?
You need to have the mapper configured, so in your test class set up, not the per-test setup, call the code to set up the mappings. Note, you'll also probably need to modify your expectation for the user service call as the arguments won't match, i.e, they are different objects. Probably you want a test that checks if the properties of the object match those of the model being passed to the method.
You should really use an interface for the mapping engine so that you can mock it rather than using AutoMapper otherwise it is an integration test not a unit test.
AutoMapper has an interface called IMappingEngine that you can inject into your controller using your IoC container like below (this example is using StructureMap).
class MyRegistry : Registry
{
public MyRegistry()
{
For<IMyRepository>().Use<MyRepository>();
For<ILogger>().Use<Logger>();
Mapper.AddProfile(new AutoMapperProfile());
For<IMappingEngine>().Use(() => Mapper.Engine);
}
}
You will then be able to use dependency injection to inject AutoMapper's mapping engine into your controller, allowing you to reference your mappings like below:
[POST("signup")]
public virtual ActionResult Signup(UserRegisterViewModel user)
{
if (ModelState.IsValid)
{
var newUser = this.mappingEngine.Map<UserRegisterViewModel, User>(user);
var confirmation = _userService.AddUser(newUser);
if (confirmation.WasSuccessful)
return RedirectToAction(MVC.Home.Index());
else
ModelState.AddModelError("Email", confirmation.Message);
}
return View(user);
}
You can read more about this here: How to inject AutoMapper IMappingEngine with StructureMap
Probably it is cool to abstract mapping into MappingEngine.
Sometimes I use following approach to IOC Automapper
In IOC builder:
builder.RegisterInstance(AutoMapperConfiguration.GetAutoMapper()).As<IMapper>();
where GetAutoMapper is:
public class AutoMapperConfiguration
{
public static IMapper GetAutoMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<OrderModelMapperProfile>();
cfg.AddProfile<OtherModelMapperProfile>();
//etc;
});
var mapper = config.CreateMapper();
return mapper;
}
}
And finally in Controller ctor
public MyController(IMapper mapper)
{
_mapper = mapper;
}

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