I have the following Unit Test:
[Fact]
public void FetchStudents_Rendered_Test()
{
var testData = new List<Student>()
{
new Student()
{
Id = 1,
Name = "Sample Name",
Email = "sample#email.com",
Phone = "123456789",
Address = "Sample address"
}
};
var mockDbSet = Mock.Of<DbSet<Student>>(dbSet => dbSet.AsQueryable() == testData.AsQueryable());
DbContextOptions<ApplicationDbContext> options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "StudentsTest")
.Options;
var mockDbContext = new Mock<ApplicationDbContext>(options);
using var ctx = new TestContext();
ctx.Services.AddSingleton<IStudentsService>(new StudentsService(mockDbContext.Object));
// RenderComponent will inject the service in the WeatherForecasts component
// when it is instantiated and rendered.
var cut = ctx.RenderComponent<FetchStudents>();
// Assert that service is injected
Assert.NotNull(cut.Instance.students);
}
The StudentService looks like below:
public class StudentsService : IStudentsService
{
private readonly ApplicationDbContext _db;
public StudentsService(ApplicationDbContext db)
{
_db = db;
}
public async Task<List<Student>> GetStudentsAsync()
{
return await _db.Students.ToListAsync();
}
}
When I run the unit test I get the following error:
System.InvalidOperationException: 'The source 'IQueryable' doesn't implement 'IAsyncEnumerable<BlazorStudentApp.Data.Models.Student>'. Only sources that implement 'IAsyncEnumerable' can be used for Entity Framework asynchronous operations.'
I have tried setting up the DbSet in multiple different ways, but I keep getting the same error.
What am I missing here?
The test method is always failing. After the Setup the method UpdateAsync should return 1 in the result but it remains always 0 which results in exception in the controller method.
Can you tell what I am missing here ?
[Test]
public async Task UpdateImportHeaderAsyncTest()
{
//Arrange
HeaderRequest request = new HeaderRequest()
{
ConfigurationId = 1,
Key = "1",
Status = 1
};
_manager.Setup(a => a.UpdateAsync(_mockData.Header)).Returns(Task.FromResult(1));
//Act
var actual = await Controller.UpdateHeaderAsync(request);
//Assert
Assert.NotNull(actual);
}
//Controller Method
[HttpPut]
public async Task<int> UpdateHeaderAsync(HeaderRequest request)
{
var result = 0;
try
{
result = await _manager.UpdateAsync(new Header()
{
HeaderId = request.Id,
Status = request.Status,
ConfigurationId = request.ConfigurationId
});
if (result == 0)
{
throw new RecordNotFoundException("No records found.", "1", "");
}
}
catch (Exception ex)
{
throw;
}
return result;
}
Loosen the argument match using It.IsAny<Header>()to get the desired behavior.
//...
_manager
.Setup(a => a.UpdateAsync(It.IsAny<Header>()))
.ReturnsAsync(1);
//...
The setup also allows for ReturnsAsync for setting up async members.
What was happening before was that you were setting it up with a specific referenced instance. That instance was not the same one used when exercising the test since you initialized a new Header. This caused the mock to return the default value for the return type.
Reference Moq Quickstart to get a better understanding of how to use the framework
I have an ASP.NET MVC Core application that I am writing unit tests for. One of the action methods uses User name for some functionality:
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
which obviously fails in the unit test. I looked around and all suggestions are from .NET 4.5 to mock HttpContext. I am sure there is a better way to do that. I tried to inject IPrincipal, but it threw an error; and I even tried this (out of desperation, I suppose):
public IActionResult Index(IPrincipal principal = null) {
IPrincipal user = principal ?? User;
SettingsViewModel svm = _context.MySettings(user.Identity.Name);
return View(svm);
}
but this threw an error as well.
Couldn't find anything in the docs either...
The controller’s User is accessed through the HttpContext of the controller. The latter is stored within the ControllerContext.
The easiest way to set the user is by assigning a different HttpContext with a constructed user. We can use DefaultHttpContext for this purpose, that way we don’t have to mock everything. Then we just use that HttpContext within a controller context and pass that to the controller instance:
var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, "example name"),
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim("custom-claim", "example claim value"),
}, "mock"));
var controller = new SomeController(dependencies…);
controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = user }
};
When creating your own ClaimsIdentity, make sure to pass an explicit authenticationType to the constructor. This makes sure that IsAuthenticated will work correctly (in case you use that in your code to determine whether a user is authenticated).
In previous versions you could have set User directly on the controller, which made for some very easy unit tests.
If you look at the source code for ControllerBase you will notice that the User is extracted from HttpContext.
/// <summary>
/// Gets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
/// </summary>
public ClaimsPrincipal User => HttpContext?.User;
and the controller accesses the HttpContext via ControllerContext
/// <summary>
/// Gets the <see cref="Http.HttpContext"/> for the executing action.
/// </summary>
public HttpContext HttpContext => ControllerContext.HttpContext;
You will notice that these two are read only properties. The good news is that ControllerContext property allows for setting it's value so that will be your way in.
So the target is to get at that object. In Core HttpContext is abstract so it is a lot easier to mock.
Assuming a controller like
public class MyController : Controller {
IMyContext _context;
public MyController(IMyContext context) {
_context = context;
}
public IActionResult Index() {
SettingsViewModel svm = _context.MySettings(User.Identity.Name);
return View(svm);
}
//...other code removed for brevity
}
Using Moq, a test could look like this
public void Given_User_Index_Should_Return_ViewResult_With_Model() {
//Arrange
var username = "FakeUserName";
var identity = new GenericIdentity(username, "");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
var model = new SettingsViewModel() {
//...other code removed for brevity
};
var mockContext = new Mock<IMyContext>();
mockContext.Setup(m => m.MySettings(username)).Returns(model);
var controller = new MyController(mockContext.Object) {
ControllerContext = new ControllerContext {
HttpContext = mockHttpContext.Object
}
};
//Act
var viewResult = controller.Index() as ViewResult;
//Assert
Assert.IsNotNull(viewResult);
Assert.IsNotNull(viewResult.Model);
Assert.AreEqual(model, viewResult.Model);
}
There is also the possibility to use the existing classes, and mock only when needed.
var user = new Mock<ClaimsPrincipal>();
_controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = user.Object
}
};
In my case, I needed to make use of Request.HttpContext.User.Identity.IsAuthenticated, Request.HttpContext.User.Identity.Name and some business logic sitting outside of the controller. I was able to use a combination of Nkosi's, Calin's and Poke's answer for this:
var identity = new Mock<IIdentity>();
identity.SetupGet(i => i.IsAuthenticated).Returns(true);
identity.SetupGet(i => i.Name).Returns("FakeUserName");
var mockPrincipal = new Mock<ClaimsPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity.Object);
var mockAuthHandler = new Mock<ICustomAuthorizationHandler>();
mockAuthHandler.Setup(x => x.CustomAuth(It.IsAny<ClaimsPrincipal>(), ...)).Returns(true).Verifiable();
var controller = new MyController(...);
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object);
controller.ControllerContext = new ControllerContext();
controller.ControllerContext.HttpContext = new DefaultHttpContext()
{
User = mockPrincipal.Object
};
var result = controller.Get() as OkObjectResult;
//Assert results
mockAuthHandler.Verify();
I want to hit my Controllers directly and just use DI like AutoFac. To do this I first registering ContextController.
var identity = new GenericIdentity("Test User");
var httpContext = new DefaultHttpContext()
{
User = new GenericPrincipal(identity, null)
};
var context = new ControllerContext { HttpContext = httpContext};
builder.RegisterInstance(context);
Next I enable property injection when I register the Controllers.
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Controller")).PropertiesAutowired();
Then User.Identity.Name is populated, and I do not need to do anything special when calling a method on my Controller.
public async Task<ActionResult<IEnumerable<Employee>>> Get()
{
var requestedBy = User.Identity?.Name;
..................
I would look to implement an Abstract Factory Pattern.
Create an interface for a factory specifically for providing user names.
Then provide concrete classes, one which provides User.Identity.Name, and one that provides some other hard coded value that works for your tests.
You can then use the appropriate concrete class depending on production versus test code. Perhaps looking to pass the factory in as a parameter, or switching to the correct factory based on some configuration value.
interface IUserNameFactory
{
string BuildUserName();
}
class ProductionFactory : IUserNameFactory
{
public BuildUserName() { return User.Identity.Name; }
}
class MockFactory : IUserNameFactory
{
public BuildUserName() { return "James"; }
}
IUserNameFactory factory;
if(inProductionMode)
{
factory = new ProductionFactory();
}
else
{
factory = new MockFactory();
}
SettingsViewModel svm = _context.MySettings(factory.BuildUserName());
I got a brownfield .net 4.8 project that I needed to convert to .net 5.0 and I wanted to keep as much of the original code as possible, including the unit-/integration tests. The test for Controllers relied on the Context a lot so I created this Extension method to enable setting tokens, claims and headers:
public static void AddContextMock(
this ControllerBase controller,
IEnumerable<(string key, string value)> claims = null,
IEnumerable<(string key, string value)> tokens = null,
IEnumerable<(string key, string value)> headers = null)
{
HttpContext mockContext = new DefaultHttpContext();
if(claims != null)
{
mockContext.User = SetupClaims(claims);
}
if(tokens != null)
{
mockContext.RequestServices = SetupTokens(tokens);
}
if(headers != null)
{
SetupHeaders(mockContext, headers);
}
controller.ControllerContext = new ControllerContext()
{
HttpContext = mockContext
};
}
private static void SetupHeaders(HttpContext mockContext, IEnumerable<(string key, string value)> headers)
{
foreach(var header in headers)
{
mockContext.Request.Headers.Add(header.key, header.value);
}
}
private static ClaimsPrincipal SetupClaims(IEnumerable<(string key, string value)> claimValues)
{
var claims = claimValues.Select(c => new Claim(c.key, c.value));
return new ClaimsPrincipal(new ClaimsIdentity(claims, "mock"));
}
private static IServiceProvider SetupTokens(IEnumerable<(string key, string value)> tokenValues)
{
var mockServiceProvider = new Mock<IServiceProvider>();
var authenticationServiceMock = new Mock<IAuthenticationService>();
var authResult = AuthenticateResult.Success(
new AuthenticationTicket(new ClaimsPrincipal(), null));
var tokens = tokenValues.Select(t => new AuthenticationToken { Name = t.key, Value = t.value });
authResult.Properties.StoreTokens(tokens);
authenticationServiceMock
.Setup(x => x.AuthenticateAsync(It.IsAny<HttpContext>(), null))
.ReturnsAsync(authResult);
mockServiceProvider.Setup(_ => _.GetService(typeof(IAuthenticationService))).Returns(authenticationServiceMock.Object);
return mockServiceProvider.Object;
}
This uses Moq but can be adapted to other mocking frameworks. The authentication type is hardcoded to "mock" since I rely on default authentication but this could be supplied as well.
It is used as such:
_controllerUnderTest.AddContextMock(
claims: new[]
{
(ClaimTypes.Name, "UserName"),
(ClaimTypes.MobilePhone, "1234"),
},
tokens: new[]
{
("access_token", "accessTokenValue")
},
headers: new[]
{
("header", "headerValue")
});
If you're using Razor pages and want to override the claims:
[SetUp]
public void Setup()
{
var user = new ClaimsPrincipal(new ClaimsIdentity(
new Claim[] {
new("dateofbirth", "2000-10-10"),
new("surname", "Smith") },
"mock"));
_razorModel = new RazorModel()
{
PageContext = new PageContext
{
HttpContext = new DefaultHttpContext() { User = user }
}
};
}
I'm trying to set up a mock of this interface:
public interface IAuthenticatedRequestService
{
HttpClient CreateHttpClientForJwt(Func<HttpResponseMessage, bool> isUnauthenticated, int timeoutSeconds);
HttpClient CreateHttpClientForAccessToken(Func<HttpResponseMessage, bool> isUnauthenticated, int timeoutSeconds);
}
This is one implementation of the method to setup that is in use and working:
public HttpClient CreateHttpClientForAccessToken(Func<HttpResponseMessage, bool> isUnauthenticated, int timeoutSeconds)
{
var client = Mvx.Resolve<IPlatformOperationProvider>().CreateHttpClient(timeoutSeconds);
return new HttpClient(new AuthenticatedHttpMessageHandler(this, client, AuthenticationUtils.AddAccessTokenToRequest, isUnauthenticated,_loggingService));
}
This is one usage of the implemented method that is working:
var client = service.CreateHttpClientForAccessToken(x => x.StatusCode == HttpStatusCode.Unauthorized, CoreConstants.TimeoutMyDetails);
This is my unit test which sets up the mock:
[Test]
public async void TestIsLoggedInIsTrue()
{
//Arrange
var authenticatedRequestService = new Mock<IAuthenticatedRequestService>();
authenticatedRequestService.Setup(foo => foo.CreateHttpClientForAccessToken((It.IsAny<Func <HttpResponseMessage, bool>>())
, 0
)).Returns(new HttpClient());
var platformOperationProvider = new Mock<IPlatformOperationProvider>();
platformOperationProvider.Setup(foo => foo.CreateHttpClient(1)).Returns(new HttpClient());
Mvx.RegisterSingleton<IPlatformOperationProvider>(platformOperationProvider.Object);
Mvx.RegisterSingleton<IAuthenticatedRequestService>(authenticatedRequestService.Object);
var loggedInProvider = new LoggedInProvider(
new Mock<ISecuredSettings>().Object,
new Mock<ILoggingService>().Object
);
//Act
await loggedInProvider.SetUserAndToken(
new User(),
new ApiAccessInfo("refresh token", "access token", "jwt")
);
//Assert
Assert.IsTrue(loggedInProvider.IsLoggedIn);
}
This unit test has no errors, but the test fails (I think it is because I am passing it any HttpResponseMessage? And I need to somehow pass it HttpStatusCode.Accepted? How would I do that?
Take note of the usage of the method, how it passes HttpStatusCode.Unauthorized, then can I do something like that with HttpStatusCode.Accepted?:
var client = service.CreateHttpClientForAccessToken(x => x.StatusCode == HttpStatusCode.Unauthorized, CoreConstants.TimeoutMyDetails);
EDIT: To be clear, It is this line of code that I need to correct:
authenticatedRequestService.Setup(foo => foo.CreateHttpClientForAccessToken((It.IsAny<Func <HttpResponseMessage, bool>>())
, 0
)).Returns(new HttpClient());
EDIT: Whilst debugging the problem starts here (check the code comment after the client is created):
async Task<ServiceResponse> UpdateUserDetails()
{
// Have to late-resolve this otherwise we end up with a dependency loop
var service = Mvx.Resolve<IAuthenticatedRequestService>();
try
{
var client = service.CreateHttpClientForAccessToken(x => x.StatusCode == HttpStatusCode.Unauthorized, CoreConstants.TimeoutMyDetails);
// here is the problem, the client is null after this line of code.
var user = _user;
I have since found that it is not a Moq issue. MvvmCross is not registering the object to resolve correctly.
This line is not working:
Mvx.RegisterSingleton<IAuthenticatedRequestService>(authenticatedRequestService.Object);
as this line creates an AuthenticatedRequestService but it is not the mock one that I made:
var service = Mvx.Resolve<IAuthenticatedRequestService>();
Here is some context of resolving the AuthenticatedRequestService
async Task<ServiceResponse> UpdateUserDetails()
{
// Have to late-resolve this otherwise we end up with a dependency loop
var service = Mvx.Resolve<IAuthenticatedRequestService>();
try
{
var client = service.CreateHttpClientForAccessToken(x => x.StatusCode == HttpStatusCode.Unauthorized, CoreConstants.TimeoutMyDetails);
var user = _user;
var str = await client.GetStringAsync(new Uri(user.IdUrl));
var newUser = JsonConvert.DeserializeObject<User.Json>(str);
var token = _token;
if (token != null)
I am running unit tests on code which uses VirtualParthUtility.GetAbsolute, but am having problems mocking the context for this to work.
I've set up a mock context with Moq as follows
private Mock<HttpContextBase> MakeMockHttpContext(string url) // url = "~/"
{
var mockHttpContext = new Mock<HttpContextBase>();
// Mock the request
var mockRequest = new Mock<HttpRequestBase>();
mockRequest.Setup(x => x.ApplicationPath).Returns("/");
mockRequest.Setup(x => x.Path).Returns("/");
mockRequest.Setup(x => x.PathInfo).Returns(string.Empty);
mockRequest.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns(url);
mockHttpContext.Setup(x => x.Request).Returns(mockRequest.Object);
// Mock the response
var mockResponse = new Mock<HttpResponseBase>();
mockResponse.Setup(x => x.ApplyAppPathModifier(It.IsAny<string>())).Returns((string s) => s);
mockHttpContext.Setup(x => x.Response).Returns(mockResponse.Object);
return mockHttpContext;
}
And attached this to an MVC Controller
_myController.ControllerContext = new ControllerContext(MakeMockHttpContext("~/").Object, new RouteData(), _slideSelectorController);
The code that runs during the test hits the line:
venue.StyleSheetUrl = VirtualPathUtility.ToAbsolute(venue.StyleSheetUrl); // input like "~/styles/screen.css"
Every time this runs, it steps into System.Web.VirtualPathUtility, with the problem that the "VirtualParthString" to be returned always throws an exception:
public static string ToAbsolute(string virtualPath)
{
return VirtualPath.CreateNonRelative(virtualPath).VirtualPathString;
}
The reason for the exception is easy to see in System.Web.VirtualPathString:
public string VirtualPathString
{
get
{
if (this._virtualPath == null)
{
if (HttpRuntime.AppDomainAppVirtualPathObject == null)
{
throw new HttpException(System.Web.SR.GetString("VirtualPath_CantMakeAppAbsolute", new object[] { this._appRelativeVirtualPath }));
}
if (this._appRelativeVirtualPath.Length == 1)
{
this._virtualPath = HttpRuntime.AppDomainAppVirtualPath;
}
else
{
this._virtualPath = HttpRuntime.AppDomainAppVirtualPathString + this._appRelativeVirtualPath.Substring(2);
}
}
return this._virtualPath;
}
}
Through the Watch Window I can see that _virtualPath and HttpRuntime.AppDomainAppVirtualPathString are both null, hence it throws an exception.
If _virtualPath were set, the exception wouldn't happen. But after the VirtualPath.Create method has created a new VirtualPath object, it doesn't set the _virtualPath property before it is returned. An extract from the Create method up to this point is:
VirtualPath path = new VirtualPath();
if (UrlPath.IsAppRelativePath(virtualPath))
{
virtualPath = UrlPath.ReduceVirtualPath(virtualPath);
if (virtualPath[0] == '~')
{
if ((options & VirtualPathOptions.AllowAppRelativePath) == 0)
{
throw new ArgumentException(System.Web.SR.GetString("VirtualPath_AllowAppRelativePath", new object[] { virtualPath }));
}
path._appRelativeVirtualPath = virtualPath;
return path;
So if anyone can suggest how to get this unit test working, that will be very helpful!
Thanks,
Steve
I would just create a wrapper interface. Something like:
public interface IPathUtilities
{
string ToAbsolute(string virtualPath);
}
You can inject that into your controller. At test time, use a stub. At runtime, you'll have a class that implements IPathUtilities and calls VirtualPathUtility.ToAbsolute().