如何使用Moq设置Mocked上下文?

时间:2017-06-22 14:44:25

标签: c# .net entity-framework moq repository-pattern

我使用洋葱架构和数据访问实体框架实现了存储库模式,现在我想使用Moq进行测试。我刚刚问了一个关于SO的问题,答案我现在更加困惑(答案很好,但是我对阅读doc后如何模拟的理解很差)。我想要做的是测试Repository方法Get(long id)。我的存储库构造函数将DbContext作为参数(称为PrincipalServerContext),因此建议我模拟上下文以测试我的Repository。让我们说这是我的Repository

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

现在我的一个POCO Web_Documents(EF实体):

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; } 
}

最后我的Repository方法(知道存储库是通用的,我使用POCO作为通用类型):

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'
    }
}

这会构建一个具有适当ID名称的表达式,因为每个表都有不同的ID名称(公司的策略)。

从我在这里被告知Should this case of Assert.AreSame return true?我明白我必须为Mock对象构建一个返回类型,但是我的上下文类很薄我没有任何方法或任何东西,只是一个DbSet。 所以我尝试了这个作为测试,但它可能没有任何意义,因为它失败了(我真的迷失了,不理解它):

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");
}

说我想做一个简单的测试来查找搜索到的ID是否与我的数据库条目相对应,我应该如何设置我想要定义的moqContext对象?在示例中,我看到他们通常有自己的模拟对象的方法,但没有,所以我发现这个Mocking DbContext for TDD Repository让我尝试了这个测试。

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

以下是使用内存数据库的示例。

首先,您创建一个模拟工作单元实例的请求。

Select count(distinct a.seller), sum(b.value)
from tab_sellers a 
left join tab_sales b
on a.id_seller = b.seller


Select count(distinct seller), sum(value)
from tab_sales

然后你建立了模拟工作单元。根据我所读到的,需要单独的上下文(一个用于播种,一个用于测试)。 MockEntityFactory只返回一个虚拟数据数组,用于填充InMemoryDatabase中的dbsets。

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

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

    //assert
    ...
}

然后我将这个工作单元通过必要的层,并且不需要做任何特殊的测试我的生产代码。