I am just starting to dive into unit testing and am just starting to grasp the repository pattern and IoC. I don't think I fully understand it, however, because parts of it seems a bit silly. Let me explain.
My Controller:
public class UserProfileController : ApiController
{
private IUserProfileRepository repository;
// Optional constructor, passes repository, allows dependency injection
public UserProfileController(IUserProfileRepository userProfileRepository)
{
this.repository = userProfileRepository;
}
// GET api/UserProfile
// Returns a list of all users
public IEnumerable<UserProfile> Get()
{
// Only Admins can see a list of users
if (Roles.IsUserInRole("Admin"))
{
return repository.Get();
}
else
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.Forbidden)
{
ReasonPhrase = "Administrator access required"
});
}
}
// Other methods, etc.
(note that I have a dependency, Roles.IsUserInRole("Admin") that I can't figure out how to abstract, which causes some problems).
My typical repo interface:
public interface IUserProfileRepository : IDisposable
{
IEnumerable<UserProfile> Get();
// Other methods, etc.
}
Repo:
public class UserProfileRepository : IUserProfileRepository, IDisposable
{
private OfootContext context;
public UserProfileRepository(OfootContext context)
{
this.context = context;
}
public IEnumerable<UserProfile> Get()
{
return context.UserProfiles.AsEnumerable();
}
// ... More code
So everything seems fine, I've abstracted my business access layer from my business logic and now I can create a fake repository to run unit tests.
Fake Repo:
public class FakeUserProfileRepository : IUserProfileRepository, IDisposable
{
private List<UserProfile> context;
public FakeUserProfileRepository(List<UserProfile> context)
{
this.context = context;
}
public IEnumerable<UserProfile> Get()
{
return context.AsEnumerable();
}
and Test:
[TestMethod]
public void GetUsers()
{
// Arrange
var items = new List<UserProfile>()
{
new UserProfile
{
UserId = 1,
Username = "Bob",
},
new UserProfile
{
UserId = 2,
Username = "Bob2",
}
};
FakeUserProfileRepository repo = new FakeUserProfileRepository(
items);
UserProfileController controller = new UserProfileController(
repo);
// Act
IEnumerable<UserProfile> result = controller.Get();
// Assert
Assert.IsNotNull(result);
}
Now that we're on the same page (and feel free to point out any 'code smells'), here are my thoughts:
If I do figure out a way to abstract the user account garbage out, I am still just shuffling around code and creating more code (maybe even twice as much? Since I need to create fakes) where I could just replace the Context.
My question is: What am I missing? Is it an overall concept or is it something specific?
What is a Repository pattern and why should we use it? With the Repository pattern, we create an abstraction layer between the data access and the business logic layer of an application. By using it, we are promoting a more loosely coupled approach to access our data from the database.
Here are some things that you should unit test in your Web API controllers: The action returns the correct type of response. Invalid parameters return the correct error response. The action calls the correct method on the repository or service layer. If the response includes a domain model, verify the model type.
No, the repository/unit-of-work pattern (shortened to Rep/UoW) isn't useful with EF Core. EF Core already implements a Rep/UoW pattern, so layering another Rep/UoW pattern on top of EF Core isn't helpful.
The Repository pattern. Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.
You're on the right track. It's always painful getting things up and running, but you'll find it pays off down the road.
Rather than create "fake" objects, I recommend a framework like Moq. It allows you to set up the behavior you need at the time of the test, rather than re-implementing whole interfaces. For example, in your test you could simply write:
Mock<IUserProfileRepository> mockUserRepo = new Mock<IUserProfileRepository>();
var items = new List<UserProfile>()
{
new UserProfile
{
UserId = 1,
Username = "Bob",
},
new UserProfile
{
UserId = 2,
Username = "Bob2",
}
};
mockUserRepo.Setup(m => m.Get().Returns(items.AsEnumerable());
UserProfileController controller = new UserProfileController(
mockUserRepo.Object);
// Act
IEnumerable<UserProfile> result = controller.Get();
//Now you can keep varying the mock response by changing the Setup(), so now
//check for null response handling, 0 items, exceptions etc...
The net result of all this effort is that you've totally isolated the testing to your Controller, there are no DB dependencies and you can easily vary the inputs without writing classes, but rather playing with the mock setup.
If you follow this simple architectural pattern, you gain awesome testability and a clear separation of concerns. As things get more complex in your system, you can take advantage of a DI container like Unity.
On the authentication piece, I recommend creating Attributes you can decorate your methods with, like ASP.Net MVC uses: [Authorization(Roles="Admin")] as an example. This creates another useful cross-cutting pattern that keeps the Auth stuff decoupled from the business logic in the controller.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With