Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web API, OData, $inlinecount and testing

I previously had a Web API controller that looked like this:

    public IQueryable<ApiDesignOverview> GetList(
        string brandIds = "", 
        string categoryIds = "", 
        string query = "",
        string categoryOp = "or")

I heard that the OData NuGet package now supports the $inlinecount OData parameter, so I tried to add it using the instructions from http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options - I don't want to have to use OData wholesale as that would entail a large amount of re-architecturing of the app, so I went for the PageResult<T> option.

So now my controller looks like this:

    public PageResult<ApiDesignOverview> GetList(
        ODataQueryOptions<ApiDesignOverview> options,
        string brandIds = "", 
        string categoryIds = "", 
        string query = "",
        string categoryOp = "or")

My problems are now:

  • How do I mock a ODataQueryOptions for unit testing?
  • If they can't be mocked, how do I create one? I need a ODataQueryContext to construct one, which requires a Microsoft.Data.Edm.IEdmModel, which requires... what? I can't find any documentation for this.

Really, it would be better if I could remove the ODataQueryOptions from the controller signature like before. Is this possible?

like image 634
Grokys Avatar asked Dec 01 '22 21:12

Grokys


2 Answers

If you do not (or cannot as in my case) want to change away from using ODataQueryOptions and PageResult, here is how you can create an ODataQueryOptions instance for unit tests:

//arrange
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/MyProject/api/Customers?$filter=CustomerID eq 1");
var controller = new CustomersController
{
    Request = request
};

ODataModelBuilder modelBuilder = new ODataConventionModelBuilder(); 
modelBuilder.EntitySet<Customer>("Customers"); 
var opts = new ODataQueryOptions<Customer>(new ODataQueryContext(modelBuilder.GetEdmModel(),typeof(Customer)), request);

//act
var result = controller.Get(opts);

//assert
Assert.AreEqual(1, result.Items.First().CustomerID);
like image 131
user1366516 Avatar answered Dec 04 '22 09:12

user1366516


If you prefer returning IQueryable and yet want support for $inlinecount, it is still possible to do that by modyifying QueryableAttribute.

public class InlineCountQueryableAttribute : QueryableAttribute
{
    private static MethodInfo _createPageResult =
        typeof(InlineCountQueryableAttribute)
        .GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
        .Single(m => m.Name == "CreatePageResult");

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        base.OnActionExecuted(actionExecutedContext);

        HttpRequestMessage request = actionExecutedContext.Request;
        HttpResponseMessage response = actionExecutedContext.Response;

        IQueryable result;
        if (response.IsSuccessStatusCode
            && response.TryGetContentValue<IQueryable>(out result))
        {
            long? inlineCount = request.GetInlineCount();
            if (inlineCount != null)
            {
                actionExecutedContext.Response = _createPageResult.MakeGenericMethod(result.ElementType).Invoke(
                    null, new object[] { request, request.GetInlineCount(), request.GetNextPageLink(), result }) as HttpResponseMessage;
            }
        }
    }

    internal static HttpResponseMessage CreatePageResult<T>(HttpRequestMessage request, long? count, Uri nextpageLink, IEnumerable<T> results)
    {
        return request.CreateResponse(HttpStatusCode.OK, new PageResult<T>(results, nextpageLink, count));
    }
}

Notice, that I am using reflection to create PageResult. You can instead return an object of your liking that can be formatted by the formatter that you use. An anonymous object with results and count will work too if you are using the Json formatter.

like image 30
RaghuRam Nadiminti Avatar answered Dec 04 '22 10:12

RaghuRam Nadiminti