Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking database in EF6 db first

I'm having trouble figuring out how to mock my database for unit testing my web api controllers. All the resources I've found work for code first EF but not db first where my context is auto-generated.

Put simply I want to be able to call my controller's CRUD operations against a fake database I create on the fly and am looking for the most straight forward method of doing this.

I'm trying to use http://www.nogginbox.co.uk/blog/mocking-entity-framework-data-context to put it together but cannot manage...

My context is defined as:

public partial class MyEntities : DbContext
{
    public MyEntities()
        : base("name=MyEntities")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

    public virtual DbSet<Company> Companies { get; set; }

Now I understand that I need to create an IContext which allows the mocking of MyEntities, but I cannot figure out how to organize this.

I've tried adding the below classes but can't figure out how to organize it past this.

public interface IContext
{
    IObjectSet<Company> Companies { get; }

    void SaveChanges();
}

public class EFContext: IContext
{
    private readonly MyEntities _data;

    public EFContext()
    {
        _data = new MyEntities();
    }

    public IObjectSet<Company> Companies
    {
        get 
        {
            return _data.CreateObjectSet<Company>();
        }
    }

    public void SaveChanges()
    {
        _data.SaveChanges();
    }
}

Example

Example controller I'd like to unit test, which would be really easy to do if I could mock a database to test against.

public IHttpActionResult Search([FromBody]string query)
    {
        var companies = CompanyRepository.Get().Where(c => c.Name.ToLower().Contains(query.ToLower()));
        var people = PersonRepository.Get().Where(c => c.FirstName.ToLower().Contains(query.ToLower()) || c.LastName.ToLower().Contains(query.ToLower()));

        List<SearchResult> results = new List<SearchResult>();
        foreach(Company company in companies)
            results.Add(new SearchResult { ID = company.ID, Name = company.Name, Type = "Company" });
        foreach (Person person in people)
            results.Add(new SearchResult { ID = person.ID, Name = person.FirstName + " " + person.LastName, Type = "Person" });

        return Ok(results);
    }
  1. How can I mock my EF database first context to use a test database?
like image 259
Joshua Ohana Avatar asked Feb 14 '26 15:02

Joshua Ohana


1 Answers

Updated 11/07/2015

So some tests that I can see are:

  1. "Search should get companies once where query is not empty"
  2. "Search should not get companies when query is empty"
  3. "Search should get people once when query is not empty"
  4. "Search should not get people when query is empty"
  5. "Search should not add any companies when query is empty"
  6. "Search should return a result with one company if one company found from company repository"
  7. "Search should return a result with two companies if two companies found from company repository"
  8. "Search should not return a result with any people when query is empty"
  9. "Search should return a result with one person if one person found from person repository"
  10. "Search should return a result with two people if two people found from person repository"

So the first thing to think about is, "What are the dependencies" of this controller. From this method I can see the CompanyRepository and the PersonRepository. These are the things you want to mock. i.e. you aren't testing them here or any of their functionality. You are testing only whats in your method.

You will want to change your Controller so you can mock them e.g.:

public class MyController : ApiController 
{
    private ICompanyRepository companyRepository;
    private IPersonRepository personRepository;

    public MyController ()
    {
        companyRepository = new CompanyRepository();
        personRepository = new PersonRepository();
    }

    public MyController (ICompanyRepository CompanyRepository, IPersonRepository PersonRepository )
    {
        companyRepository = CompanyRepository;
        personRepository = PersonRepository;
    }
}

Your code then needs to reference the private repositories

public IHttpActionResult Search([FromBody]string query)
{
    var companies = companyRepository.Get().Where(c => c.Name.ToLower().Contains(query.ToLower()));
    var people = personRepository.Get().Where(c => c.FirstName.ToLower().Contains(query.ToLower()) || c.LastName.ToLower().Contains(query.ToLower()));

    List<SearchResult> results = new List<SearchResult>();
    foreach(Company company in companies)
        results.Add(new SearchResult { ID = company.ID, Name = company.Name, Type = "Company" });
    foreach (Person person in people)
        results.Add(new SearchResult { ID = person.ID, Name = person.FirstName + " " + person.LastName, Type = "Person" });

    return Ok(results);
}

Your tests then look something like (depending on which test & mocking framework you use). Please note this is quite rough coding and won't work if pasted!:

[TestClass]
public class MyControllerTests
{
    Mock<ICompanyRepository>() mockCompanyRepository = new Mock<ICompanyRepository>()
    Mock<IPersonRepository>() mockPersonRepository = new Mock<IPersonRepository>()
    MyController sut;

    Public MyControllerTests ()
    {
        // Create your "System under test" which is your MyController. Pass in your mock repositories to aid your testing.
        sut = new MyController(mockCompanyRepository.Object, mockPersonRepository.Object)
    }

    [TestMethod]
    public void SearchShouldGetCompaniesOnceWhereQueryIsNotEmpty
    {
        //Arrange
        var expectedCompanies = new List<Company>{new Company{"Company1"}, new Company{"Company2"}};
        //Setup mock that will return People list when called:
        mockCompanyRepository.Setup(x => x.Get()).Returns(expectedCompanies);
        mockPersonRepository.Setup(x => x.Get()).Returns(new List<Person>())

    //Act
    var result = sut.Search("Conway");

    //Assert - check the company repository was called once and the result was as expected
    mockCompanyRepository.Verify(x => x.Get(), Times.Once());

OkNegotiatedContentResult<string> conNegResult = Assert.IsType<OkNegotiatedContentResult<string>>(actionResult);
Assert.Equal("data: [{Name : "Company1"}, {Name : "Company2"}]", conNegResult.Content);
    }
}

You haven't included the code for your repositories in the question but if you use the above style you should be able to follow the same pattern. If your repository Get method has no logic in and is simply a pass through, then I would argue you don't need to write a test against it. If you are doing funky things like ordering, filtering etc etc then I would say you do. But you then test the repository by itself and in a very similar way to the above!

Quick note. The MyController constructor has 2 methods. One is parameterless and the other takes your repositories. I have done this as I do not know if you are using an IoC (Inversion of Control or Dependency Injection container). You don't need to but it will save you having to write parameterless constructors for all the classes you want to test.

Original Answer

I think the first question is "What are you trying to test?". When you write a test it should be testing just the functionality in the test target and not in any dependencies. So arguably you shouldn't need to connect to a test database to run your unit test (unless you are doing an integration test). You will need to mock out the EF DBContext however. Check this MSDN article which has plenty of examples

like image 141
sarin Avatar answered Feb 16 '26 05:02

sarin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!