diff --git a/AspNetCoreOData.sln b/AspNetCoreOData.sln
index e019d258e..3fa695aa6 100644
--- a/AspNetCoreOData.sln
+++ b/AspNetCoreOData.sln
@@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataDynamicModel", "sample
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ODataSampleCommon", "sample\ODataSampleCommon\ODataSampleCommon.csproj", "{647EFCFA-55A7-4F0A-AD40-4B6EB1BFCFFA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ODataRoutingSample.Tests", "sample\ODataRoutingSample.Tests\ODataRoutingSample.Tests.csproj", "{2084B1F8-5122-41A1-8735-06F36E7C9903}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +65,10 @@ Global
{647EFCFA-55A7-4F0A-AD40-4B6EB1BFCFFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{647EFCFA-55A7-4F0A-AD40-4B6EB1BFCFFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{647EFCFA-55A7-4F0A-AD40-4B6EB1BFCFFA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2084B1F8-5122-41A1-8735-06F36E7C9903}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2084B1F8-5122-41A1-8735-06F36E7C9903}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2084B1F8-5122-41A1-8735-06F36E7C9903}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2084B1F8-5122-41A1-8735-06F36E7C9903}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -76,6 +82,7 @@ Global
{BDC5474B-9511-4CDF-83FE-376C7130F7F0} = {B1F86961-6958-4617-ACA4-C231F95AE099}
{CE04E38B-547F-46C0-ABE4-F981E3A1874F} = {B1F86961-6958-4617-ACA4-C231F95AE099}
{647EFCFA-55A7-4F0A-AD40-4B6EB1BFCFFA} = {B1F86961-6958-4617-ACA4-C231F95AE099}
+ {2084B1F8-5122-41A1-8735-06F36E7C9903} = {B1F86961-6958-4617-ACA4-C231F95AE099}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {540C9752-AAC0-49EA-BA60-78490C90FF86}
diff --git a/sample/ODataRoutingSample.Tests/AccountsControllerTests.cs b/sample/ODataRoutingSample.Tests/AccountsControllerTests.cs
new file mode 100644
index 000000000..5cc9166aa
--- /dev/null
+++ b/sample/ODataRoutingSample.Tests/AccountsControllerTests.cs
@@ -0,0 +1,88 @@
+using System.Linq;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.OData;
+using Microsoft.AspNetCore.OData.Query;
+using Microsoft.Extensions.Logging;
+using Microsoft.OData.Edm;
+using Microsoft.OData.ModelBuilder;
+using Microsoft.OData.UriParser;
+using ODataRoutingSample.Controllers;
+using ODataRoutingSample.Models;
+using Xunit;
+
+namespace ODataRoutingSample.Tests
+{
+ ///
+ /// This is a suggestion how to unit test a controller which will be very useful for the devs
+ ///
+ public class AccountsControllerTests
+ {
+ // It is not mocked because it is not used
+ private readonly ILogger _mockedLogger = null;
+ private AccountsController _accountsController;
+
+ public AccountsControllerTests()
+ {
+ _accountsController = new AccountsController(_mockedLogger);
+ }
+
+ [Fact]
+ public void AccountsController_GetTopTwoAccounts_ShouldReturnTopTwoAccounts()
+ {
+ // Arrange
+ var modelBuilder = new ODataConventionModelBuilder();
+ modelBuilder.EntitySet("Account");
+ var edmModel = modelBuilder.GetEdmModel();
+
+ var options = new ODataOptions();
+ options.AddModel("odata", EdmCoreModel.Instance);
+ options.AddModel("my{data}", edmModel);
+
+ HttpRequest request = RequestFactory.Create("GET",
+ "http://localhost/api?$top=2&$count=true",
+ dataOptions => dataOptions.AddModel("odata", edmModel));
+
+ var oDataQueryContext = new ODataQueryContext(edmModel, typeof(Account), new ODataPath());
+
+ var aDataQueryOptions = new ODataQueryOptions(oDataQueryContext, request);
+
+ // Act
+ _accountsController = new AccountsController(_mockedLogger);
+ var result = _accountsController.Get(aDataQueryOptions);
+ var accounts = (IQueryable) ((OkObjectResult) result).Value;
+
+ // Assert
+ Assert.Equal(2, accounts.Count());
+ }
+
+ [Fact]
+ public void AccountsController_SelectAccountWithNameEqualHot_ShouldReturnAccountWithHotName()
+ {
+ // Arrange
+ var modelBuilder = new ODataConventionModelBuilder();
+ modelBuilder.EntitySet("Account");
+ var edmModel = modelBuilder.GetEdmModel();
+
+ var options = new ODataOptions();
+ options.AddModel("odata", EdmCoreModel.Instance);
+ options.AddModel("my{data}", edmModel);
+
+ HttpRequest request = RequestFactory.Create("GET",
+ "http://localhost/api?filter=Name eq 'Hot'",
+ dataOptions => dataOptions.AddModel("odata", edmModel));
+
+ var oDataQueryContext = new ODataQueryContext(edmModel, typeof(Account), new ODataPath());
+
+ var aDataQueryOptions = new ODataQueryOptions(oDataQueryContext, request);
+
+ // Act
+ _accountsController = new AccountsController(_mockedLogger);
+ var result = _accountsController.Get(aDataQueryOptions);
+ var accounts = (IQueryable)((OkObjectResult)result).Value;
+
+ // Assert
+ Assert.Equal("Hot", accounts.FirstOrDefault()?.Name);
+ }
+ }
+}
diff --git a/sample/ODataRoutingSample.Tests/ODataRoutingSample.Tests.csproj b/sample/ODataRoutingSample.Tests/ODataRoutingSample.Tests.csproj
new file mode 100644
index 000000000..407604c30
--- /dev/null
+++ b/sample/ODataRoutingSample.Tests/ODataRoutingSample.Tests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net5.0
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/sample/ODataRoutingSample.Tests/RequestFactory.cs b/sample/ODataRoutingSample.Tests/RequestFactory.cs
new file mode 100644
index 000000000..ecee5a648
--- /dev/null
+++ b/sample/ODataRoutingSample.Tests/RequestFactory.cs
@@ -0,0 +1,35 @@
+using System;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.OData;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace ODataRoutingSample
+{
+ public static class RequestFactory
+ {
+ ///
+ /// Creates the with OData configuration.
+ ///
+ /// The http method.
+ /// The http request uri.
+ /// The HttpRequest.
+ public static HttpRequest Create(string method, string uri, Action setupActio)
+ {
+ HttpContext context = new DefaultHttpContext();
+ HttpRequest request = context.Request;
+
+ IServiceCollection services = new ServiceCollection();
+ services.Configure(setupActio);
+ 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;
+ }
+ }
+}