Entity Framework 4.1 Fake DbContext for Testing

.net asp.net-mvc entity-framework tdd unit-testing

Question

I'm testing using this tutorial's Fake DbContext: http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/

But in order to utilize it in my Controllers, I need to modify the FakeMainModuleContext implementation:

public class FakeQuestiona2011Context : IQuestiona2011Context
{
    private IDbSet<Credencial> _credencial;
    private IDbSet<Perfil> _perfil;
    private IDbSet<Apurador> _apurador;
    private IDbSet<Entrevistado> _entrevistado;
    private IDbSet<Setor> _setor;
    private IDbSet<Secretaria> _secretaria;
    private IDbSet<Pesquisa> _pesquisa;
    private IDbSet<Pergunta> _pergunta;
    private IDbSet<Resposta> _resposta;

    public IDbSet<Credencial> Credencial { get { return _credencial ?? (_credencial = new FakeDbSet<Credencial>()); } set { } }
    public IDbSet<Perfil> Perfil { get { return _perfil ?? (_perfil = new FakeDbSet<Perfil>()); } set { } }
    public IDbSet<Apurador> Apurador { get { return _apurador ?? (_apurador = new FakeDbSet<Apurador>()); } set { } }
    public IDbSet<Entrevistado> Entrevistado { get { return _entrevistado ?? (_entrevistado = new FakeDbSet<Entrevistado>()); } set { } }
    public IDbSet<Setor> Setor { get { return _setor ?? (_setor = new FakeDbSet<Setor>()); } set { } }
    public IDbSet<Secretaria> Secretaria { get { return _secretaria ?? (_secretaria = new FakeDbSet<Secretaria>()); } set { } }
    public IDbSet<Pesquisa> Pesquisa { get { return _pesquisa ?? (_pesquisa = new FakeDbSet<Pesquisa>()); } set { } }
    public IDbSet<Pergunta> Pergunta { get { return _pergunta ?? (_pergunta = new FakeDbSet<Pergunta>()); } set { } }
    public IDbSet<Resposta> Resposta { get { return _resposta ?? (_resposta = new FakeDbSet<Resposta>()); } set { } }

    public void SaveChanges()
    {
        // do nothing (probably set a variable as saved for testing)
    }
}

And here's how I tested:

[TestMethod]
public void IndexTest()
{
    IQuestiona2011Context fakeContext = new FakeQuestiona2011Context();
    var mockAuthenticationService = new Mock<IAuthenticationService>();

    var apuradores = new List<Apurador>
    {
        new Apurador() { Matricula = "1234", Nome = "Acaz Souza Pereira", Email = "acaz@telecom.inf.br", Ramal = "1234" },
        new Apurador() { Matricula = "4321", Nome = "Samla Souza Pereira", Email = "samla@telecom.inf.br", Ramal = "4321" },
        new Apurador() { Matricula = "4213", Nome = "Valderli Souza Pereira", Email = "valderli@telecom.inf.br", Ramal = "4213" }
    };
    apuradores.ForEach(apurador => fakeContext.Apurador.Add(apurador));

    ApuradorController apuradorController = new ApuradorController(fakeContext, mockAuthenticationService.Object);
    ActionResult actionResult = apuradorController.Index();

    Assert.IsNotNull(actionResult);
    Assert.IsInstanceOfType(actionResult, typeof(ViewResult));

    ViewResult viewResult = (ViewResult)actionResult;

    Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(IndexViewModel));

    IndexViewModel indexViewModel = (IndexViewModel)viewResult.ViewData.Model;

    Assert.AreEqual(3, indexViewModel.Apuradores.Count);
}

Do I have this right?

1
46
8/1/2011 8:36:32 PM

Accepted Answer

Unfortunately, since that article is inaccurate, you are not doing it correctly. It makes out thatFakeContext will not enable unit testing for your code. when you revealIDbSet or IQueryable You can never be certain that your unit test really checks your function if you fake the set using an in-memory collection and connect it to your controller. Writing a LINQ query in your controller that will pass your unit test is simple because:FakeContext but fails at runtime because it utilizes LINQ-to-Objects (because your real context uses LINQ-to-Entities). That negates the whole point of your unit testing.

In my view, if you want to expose sets to controller, don't bother with fake context. Use actual databases for testing instead, along with integration tests. The only method to verify that LINQ queries specified in a controller function as expected is in that manner.

Yes, feel free to dial simplyToList or FirstOrDefault your settings on yourFakeContext will be useful, but if you start doing anything more complicated, you could uncover a trap quite quickly (just put the string Not able to be converted into a store expression into Google - all these problems will appear only when you run Linq-to-entities but they will pass your tests with Linq-to-objects).

Since this is a frequently asked subject, here are some more examples:

123
5/23/2017 12:10:13 PM

Popular Answer

"Unfortunately you are not doing it right because that article is wrong. It pretends that FakeContext will make your code unit testable but it will not"

You are referring to a blog article that I wrote. I believe that this issue stems from an incorrect understanding of the foundations of N-Layered unit testing. It is not the intent of my article to be used to directly verify controller logic.

A unit test should test 'One Unit,' as the name suggests. I completely forget about the data access while I'm testing a controller, like you are doing above. I should replace every call to database context in my head with a call to a black box method, acting as if I had no prior knowledge of those actions. I'm interested in evaluating the code that surrounds those operations.

Example:

I've implemented the repository pattern in my MVC application. I have a repository that will handle all of my Customer database activities, let's call it CustomerRepository: ICustomerRepository.

There are a lot of "units" in this pipeline, zzz-23 zzz. To test the controller functionality independently, you need build a phony repository that implements ICustomerRepository.

To the best of my understanding, this cannot be accomplished using just the database context. (with the possible exception of using Microsoft Moles, which you are welcome to check out.) This is due to the fact that none of your controller class's queries are run in context.

How would I go about testing the CustomerRepository logic? The simplest method is to create a false context. This will enable me to ensure that, for example, when I attempt to obtain a client by ID, it really does so. The "Cannot be transformed into a store expression" issue seldom occurs since repository techniques are so straightforward. Though in a few small instances it could (sometimes as a result of poorly designed linq queries), in these situations it is crucial to additionally run integration tests that will verify your function up till the database. In-depth testing for integration will reveal these issues. Since I've been using this N-Layered approach, there haven't been any issues.

Assemblies tests

Although testing your application against the database itself is obviously expensive and a pain if you have tens of thousands of tests, it does the greatest job of simulating how the code would be utilized in the "real world." These tests, which include everything from the user interface to the database, are crucial as well and will be carried out as part of integration tests, NOT unit tests.

Acaz, a mockable or fakeable repository is what you really need in your circumstance. Your controllers should be taking in an object that covers the database functions if you want to test them as you are. Then you can test every part of your controller's functioning by having it return anything you need it to.

watch zzz-56 zzz

You should either fake the context or use something like to the "Moles" framework in order to test the repository itself (debated whether required in all circumstances).

Testing LINQ is difficult by nature. Although it provides us significant flexibility, the fact that the query is specified outside of the context via extension methods makes testing a pain. The solution to this issue is to wrap your context in a repository.

Sorry for the delay:



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow