Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repository Pattern and Unit Testing ASP.NET Web API

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:

  1. The fake repository requires that I re-implement all of my Entity Framework logic and change it to dealing with a List object. This is more work and more links in the chain that I will need to debug.
  2. If the unit test does pass, it doesn't say anything about my code that accesses EF, so my application could still fail. This just means I need to test my EF code separately and have it hit a database.
  3. From #1, if the unit test is not testing the EF code, then it's just dealing with my authentication, authorization, and User creation code in my controller. Which it can't, because the WebSecurity and Roles classes hit my database.
  4. If I'm going to use a database to test EF code (point #2) and need a database to test the controller code (for authentication and authorization, point #3) then why bother even abstracting with a repository. Why don't I just abstract the context (using IContext or something?) and wire in a test database that is populated using the DropCreateDatabaseAlways class?

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?

like image 649
brudert Avatar asked Jan 25 '13 23:01

brudert


People also ask

What is repository pattern in Web API?

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.

What is unit testing in Web API?

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.

Do we need repository pattern with Entity Framework Core?

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.

What is repository pattern?

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.


1 Answers

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.

like image 117
hoserdude Avatar answered Sep 21 '22 18:09

hoserdude