Galkin
Galkin

Reputation: 843

Mocking or creating pragmatically ODataQueryOptions for .NET 5 Core Controller unit test

I am migration the our existing API to .net 5 and I face the issue of the unit tests migration. I want to keep tests and test controllers but I could not find a way hot to deal with ODataQueryOptions because this class has been change and I could not find a way to create a ODataQueryOptions any more. All topics which are related to my issue are outdated.

This is the old code how the controller has been tested before the migration

_userController.Request = new HttpRequestMessage(HttpMethod.Get, new Uri("http://localhost/api?$orderby=UserName desc"));
            _queryOptionsUser = new ODataQueryOptions<User>(_queryOptionsUser.Context, _userController.Request);
            PageResult<UserDto> users = _userController.GetUsers(Constants.ValidOrgCode1, _queryOptionsUser);

I would be more than thankful if someone can provide the snipes how to deal with similar issue and be able to test a controller.

Upvotes: 3

Views: 2409

Answers (2)

Alex Peck
Alex Peck

Reputation: 4711

Galkin's answer works for v8.x. I am stuck on Microsoft.AspNetCore.OData v7.7 and came up with this (critical part for me was having the route builder to call EnableDependencyInjection):

public static HttpRequest CreateHttpRequest(string uri)
{
    IServiceCollection serviceCollection = new ServiceCollection();
    serviceCollection.AddOData();

    IApplicationBuilder appBuilder = new ApplicationBuilder(serviceCollection.BuildServiceProvider());
    IRouteBuilder routeBuilder = new RouteBuilder(appBuilder);
    routeBuilder.EnableDependencyInjection();

    HttpContext context = new DefaultHttpContext();
    context.RequestServices = routeBuilder.ServiceProvider;

    HttpRequest request = context.Request;

    request.Method = "GET";
    var requestUri = new Uri(uri);
    request.Scheme = requestUri.Scheme;
    request.Host = new HostString(requestUri.Host);
    request.QueryString = new QueryString(requestUri.Query);
    request.Path = new PathString(requestUri.AbsolutePath);

    return request;
}

Then create ODataQueryOptions like this:

    requestUri = "http://localhost/odata/foos/?$select=Id,Name";
    var path = new Microsoft.AspNet.OData.Routing.ODataPath();
    var model = CreateEdmModel<FooRow>("foos");
    ODataQueryContext context = new ODataQueryContext(model, typeof(FooRow), path);

    var request = CreateHttpRequest(requestUri);

    var options = new ODataQueryOptions(context, request);

With

    private static IEdmModel CreateEdmModel<TEntity>(string entitySetName) where TEntity : class
    {
        var builder = new ODataConventionModelBuilder();
        builder.EntitySet<TEntity>(entitySetName);
        return builder.GetEdmModel();
    }

    public class FooRow
    {
        public Guid Id { get; set; }

        public string Name { get; set; }
    }

Upvotes: 1

Galkin
Galkin

Reputation: 843

Finally, I ended doing this.

Have the method of the controller to unit test

public PageResult<User> GetAll(ODataQueryOptions<User> odataQuery) 

Unit testing

// Arrange
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<User>("User");
var edmModel = modelBuilder.GetEdmModel();

const string routeName = "odata";
IEdmEntitySet entitySet = edmModel.EntityContainer.FindEntitySet("User");
ODataPath path = new ODataPath(new EntitySetSegment(entitySet));

var request = RequestFactory.Create("GET",
    "http://localhost/api?$top=10&",
    dataOptions => dataOptions.AddModel(routeName, edmModel));

request.ODataFeature().Model = edmModel;
request.ODataFeature().Path = path;
request.ODataFeature().PrefixName = routeName;

var oDataQueryContext = new ODataQueryContext(edmModel, typeof(User), new ODataPath());
var aDataQueryOptions = new ODataQueryOptions<User>(oDataQueryContext, request);

// Act
PageResult<User> users = _userController.GetAll(aDataQueryOptions);

You will need the helper class to generate HttpRequest

public static class RequestFactory
    {
        /// <summary>
        /// Creates the <see cref="HttpRequest"/> with OData configuration.
        /// </summary>
        /// <param name="method">The http method.</param>
        /// <param name="uri">The http request uri.</param>
        /// <param name="setupAction"></param>
        /// <returns>The HttpRequest.</returns>
        public static HttpRequest Create(string method, string uri, Action<ODataOptions> setupAction)
        {
            HttpContext context = new DefaultHttpContext();
            HttpRequest request = context.Request;

            IServiceCollection services = new ServiceCollection();
            services.Configure(setupAction);
            context.RequestServices = services.BuildServiceProvider();

            request.Method = method;
            var requestUri = new Uri(uri);
            request.Scheme = requestUri.Scheme;
            request.Host = requestUri.IsDefaultPort ? new HostString(requestUri.Host) : new HostString(requestUri.Host, requestUri.Port);
            request.QueryString = new QueryString(requestUri.Query);
            request.Path = new PathString(requestUri.AbsolutePath);
            
            return request;
        }

it was taken from here https://github.com/OData/AspNetCoreOData/blob/master/test/Microsoft.AspNetCore.OData.Tests/Extensions/RequestFactory.cs

I hope it will help other who will face the same issue

Upvotes: 9

Related Questions