0

I have implemented a Repository Pattern using an onion Architecture and Entity Framework for Data Access, and now I want to test it using Moq. I just asked a question on SO and with the answer I am now more confused (the answer was good but I have very poor comprehension of how to mock even after reading doc). What I want to do is test Repository method Get(long id). My repository constructor takes a DbContext as parameter(called PrincipalServerContext, so I was suggested to mock the context in order to test my Repository. Let's say this is my Repository:

public class PrincipalServerContext : DbContext
{
    public DbSet<Web_Documents> WebDoc { get; set; }
    public PrincipalServerContext()
        : base("name=PrincipalServerDB")
    {
        Database.SetInitializer(new Initializer());
    }
 }

Now one of my POCOs Web_Documents (EF entity):

public class Web_Documents
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long IDDocument { get; set; }

    [Required]
    [MaxLength(255)]
    public string NomDocument { get; set; }

    [Required]
    public long IDCategorie { get; set; }

    [ForeignKey("IDCategorie")]
    public Web_Categories cat { get; set; }
    [Required]
    [MaxLength(255)]
    public string Lien { get; set; }

    [MaxLength(50)]
    public string Type { get; set; }

    public virtual ICollection<Web_Profils> Profils { get; set; } 
}

And finally my Repository method (knowing that repository is generic, I use POCOs as Generic Types):

public T Get(long id)
{
    ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext;
    ObjectSet<T> set = objContext.CreateObjectSet<T>();
    IEnumerable<string> keyNames = set.EntitySet.ElementType
                                                .KeyMembers
                                                .Select(k => k.Name);
    if (keyNames.Count() > 1)
        return null;
    else
    {
        string idName = keyNames.ElementAt(0); // For Document would be IDDocument
        var parameter = Expression.Parameter(typeof(T));
        var property = Expression.Property(parameter, idName);
        var idValue = Expression.Constant(id, id.GetType());
        var equal = Expression.Equal(property, idValue);
        var predicate = Expression.Lambda<Func<T, bool>>(equal, parameter);
        return entities.SingleOrDefault(predicate);
        //Returns the corresponding entity to 'id' and 'T'
    }
}

This builds an expression with appropriate ID names because every table has a different ID Name (company's policy).

From what I was told here Should this case of Assert.AreSame return true? I understand that I have to build a return type for the Mock object, but my context class is so thin I don't have any methods or anything, just a DbSet. So I tried this as a test but it probably makes no sense since it failed (I'm just really lost and don't understand it):

Mock<PrincipalServerContext> moqContext;
public void IdExists(){
    moqContext = new Mock<PrincipalServerContext>();
    var set = new Mock<DbSet<Web_Documents>>();
    moqContext.Setup(c => c.Set<Web_Documents>()).Returns(set.Object);
    repoDoc = new Repository<Web_Documents>(moqContext.Object);
    var testDoc = repoDoc.Get(1L);
    Assert.AreEqual(testDoc.NomDocument, "Ajouter une catégorie");
}

Say I would want to make a simple test to find if the ID searched corresponds to my DB entry, how should I set the moqContext object I am trying to define? In examples I see they usually have methods for their mocked objects, but here none, so I found this Mocking DbContext for TDD Repository which made me try this test.

Thanks for your help!

Balagurunathan Marimuthu
  • 2,927
  • 4
  • 31
  • 44
Flexabust Bergson
  • 732
  • 14
  • 34
  • 1
    Consider keeping your Repository thin and Mocking at that level. if the repository does not have business logic then it serves as a good cut-off point for unit testing. This is my justification for using the repository pattern, so I don't have to try mocking a context. Use integration (end to end) tests to assert that for a known data state your code is functioning as expected. This way unit tests can be run rapidly (TDD) while integration tests still cover your end to end. – Steve Py Jun 22 '17 at 21:45
  • @StevePy So are you saying I do not need to mock my context to test these methods, and just make classic unit tests? For integration tests I will test the controller in my MVC project which uses the repository pattern, but for now I still want to make sure everything is functional – Flexabust Bergson Jun 23 '17 at 08:11
  • 1
    If the repositories are thin in that they simply return entities based on simple criteria or save/delete those entities then they don't benefit much from a unit test. For instance a "Get" by ID, there is no logic, it's a pass-through to the ORM. What you might want to test on a repository is if the repository adds any lower-level criteria like authorization checks and the like. Those are things that can be covered during integration runs. The real logic is in the controllers and services, mocking the repository makes those classes much easier to test, where you will be testing them frequently. – Steve Py Jun 23 '17 at 09:01
  • 1
    I found recent guidance from the EF Core team to utilize the in memory database when mocking contexts, which I had success with. Using this method you can create a mock context which uses an in memory database, and then you can seed data into the different DbSet's in your context. Then you can pass that context around and the queries execute against that. If you are interested I'll take the time to give an example. – MORCHARD Jun 23 '17 at 17:42
  • @MORCHARD Yes would definitely be interested in having an example!! Thanks! – Flexabust Bergson Jun 26 '17 at 07:42

1 Answers1

1

Here's an example of using the in-memory database.

First you create an request an instance of the mock unit of work.

[TestMethod]
public async Task ExampleTest() {
    //arrange
    Mock<IUnitOfWork> mockUow = MockUowFactory.Get(nameof(ExampleTest));

    //act
    using (var app = YOURAPP(mockUow.Object)){
         app.METHODUNDERTEST();
    }

    //assert
    ...
}

Then you build up the mock unit of work. According to what I've read the separate context is needed (one for seeding, one for testing). The MockEntityFactory just returns an array of dummy data which is used to populate the dbsets in our InMemoryDatabase.

    public class MockUowFactory {

    public static Mock<IUnitOfWork> Get(string dbName) {

        DbContextOptions<YOUR CONTEXT> options = new DbContextOptionsBuilder<YOUR CONTEXT>()
            .UseInMemoryDatabase(databaseName: dbName)
            .Options;


        using (var seedContext = new YOURCONTEXT(options)) {

            seedContext.YOURENTITY.AddRange(MockEntityFactory.YOURENTITY);

            seedContext.SaveChanges();
        }

        var context = new YOURCONTEXT(options);
        var mockUow = new Mock<IUnitOfWork>();
        mockUow.Setup(m => m.Context).Returns(context);
        mockUow.Setup(m => m.Save()).Returns(() => context.SaveChanges().ToString());

        return mockUow;
    }
}

I then pass this unit of work through the necessary layers and don't need to do anything special to test my production code.

MORCHARD
  • 263
  • 2
  • 12