Unit testing controllers with User dependency on .Net Core

Rudi Rocha
3 min readFeb 9, 2021

--

Photo by James Harrison on Unsplash

Trying .Net Core 5.0, I’ve set some playground rules. One of them is “let’s do this with Test Driven Development”, cool right?

After reading some pages about clean architecture I´ve defined a few projects:

  • Domain: a class lib project with some model classes
  • Domain.Tests.Unit: a Xunit project for Domain project-related unit tests
  • Application: a class lib project with some business logic classes
  • Application.Tests.Unit: a Xunit project for Application project-related unit tests
  • WebApi: a WebApi project with few Controller classes for the Request and Responses of my API
  • WebApi.Tests.Unit: a Xunit project for WebApi project-related unit tests

Nothing fancy!

Doing TDD for simple CRUD operations was very straight forward. Using Moq NuGet package for mocks and doing the // Arrange , // Act and // Assert approach the unit test seems like:

[Fact]
public
void CreateLesson_WhenUserIsLoggedIn_ThenTeacherEmailIsDefined()
{
// Arrange test data
var requestEntity = new Lesson() {Name = "C# Lessons"};

var controllerDependency = new Mock<ILessonsService>();
// Setup mocked functions here
var controllerToBeTested = new LessonsController(controllerDependency.Object);

// Act - endpoint should return a Created HTTP Response
var response = Assert.IsType<CreatedResult>(
controllerToBeTested.CreateLesson(requestEntity)
);

// Assert
var createdEntity = Assert.IsType<Lesson>(response.Value);
// Assert the loggedIn user Email is defined on the child model
Assert.Equal("sampleEmail@oneemail.com", createdEntity.Teacher.Email);
}

It’s a very simple test. And the Controller Action looks like this:

[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Authorize]
public IActionResult CreateLesson([FromBody] Lesson newProperty)
{
var
teacherEmail = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value;
newLesson.Teacher = new Teacher() { Email = teacherEmail };
var lesson = _lessonsService.CreateLesson(newLesson);
return Created("", lesson);
}

The code is also very simple. It gets the logged-in user’s email, creates the child model using that email, and asks the business logic to create it (very trivial in a lot of applications).

The problem

The User class, a ClaimsPrincipal class, available for you to get all the attributes from the logged-in users, is not directly accessible for you to set:

controllerToBeTested.User; // is a get only property

So how to define this data so I can make my test pass?

The way I solved it

The User class is defined when the controller’s HttpContext class is defined for the incoming request. So how to define the Claims when preparing the unit tests dependencies? the answer: when we instantiate a new controller:

[Fact]
public
void CreateEntity_WhenUserIsLoggedIn_ThenTeacherEmailIsDefined()
{
// Arrange test data
var requestEntity = new Lesson() {Name = "C# Lessons"};

var controllerDependency = new Mock<ILessonsService>();
// Setup mocked functions here
// 1. Set the HttpContext so the ControllerContext is not null
var controllerToBeTested = new LessonsController(controllerDependency.Object){
ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext()
{
User = new ClaimsPrincipal()
}
}
};
// 2. Set all the claims you need the user to have.
controllerToBeTested.User.AddIdentity(
new ClaimsIdentity(
new List<Claim>()
{
new Claim(ClaimTypes.Email, "teacherEmail@email.com")
}
)
);

and done, the user dependency is now ready to be enrolled on your TDD process.
Hope it helps!

--

--

Rudi Rocha
Rudi Rocha

Written by Rudi Rocha

Software Engineering Manager and Software Engineer | Server Side Trainer | Human stuff as a hobby.

No responses yet