EF Moq单元测试,不确定验证

时间:2017-05-24 17:01:49

标签: c# entity-framework unit-testing moq

我对单元测试完全不熟悉。我从不同的人那里读了许多“教程”,我决定使用msdn解决方案。

我使用此https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx作为我的测试,我感兴趣的是“测试非查询方案”。

根据这篇文章,我尝试测试我的简单CRUD的 Create()操作。

这是我的代码( FinancialAssistantEntities 是我的 DbContext (EF数据库优先)):

上下文

public partial class FinancialAssistantEntities : DbContext
{
    public FinancialAssistantEntities()
        : base("name=FinancialAssistantEntities")
    {
    }
    .
    .
    .
    public virtual DbSet<FAWallet> FAWallet { get; set; }
}

存储库方法(我注释掉了我的事务的使用,因为从test方法运行它会导致错误“无法找到名为'FinancialAssistantEntities'的连接字符串'应用程序配置文件。“),

public async Task<bool> CreateWallet(FAWallet model)
{
    using (var context = Context)
    {
        // transaction with IsolationLevel
        //using (var tran = context.Database.BeginTransaction(IsolationLevel.ReadUncommitted))
        {
            try
            {                       
                context.FAWallet.Add(model);
                //context.SaveChanges();
                await context.SaveChangesAsync();
                //tran.Commit();
                return true;
            }
            catch (Exception ex)
            {
                //tran.Rollback();
                throw ex;
            }
        }
    }
}

TestMethod的:

[TestMethod]
public void CreateWalletTest()
{
    var wallet = new FAWallet()
    {
        WalletId = Guid.NewGuid(),
        //WalletName = StringHelper.GenerateRandomString(12),
        // admin ID
        WalletUserId = "e6888245-1d9b-431c-a068-aa62932e47ec",
        WalletCreateDate = DateTime.Now,
        WalletEnabled = true
    };

    var mockSet = new Mock<DbSet<FAWallet>>();

    var mockContext = new Mock<FinancialAssistantEntities>();
    mockContext.Setup(x => x.FAWallet).Returns(mockSet.Object);

    var walletRepository = new FAWalletRepository(mockContext.Object);
    walletRepository.CreateWallet(wallet).Wait();

    mockSet.Verify(x => x.Add(It.IsAny<FAWallet>()), Times.Once());
    mockContext.Verify(x => x.SaveChangesAsync(), Times.Once()); 
}

首先,我不知道评论交易的使用是否是个好主意,虽然我对测试还不太了解。

其次,我的测试总是通过。我甚至评论了 WalletName 属性的集合,因为这个字段不可为空,所以我觉得我做错了。

1 个答案:

答案 0 :(得分:3)

前言

在我们开始研究你的问题的各个部分之前,让我明确一下,主要问题不在于单元测试。相反,它涉及Object Oriented Programming和一些分析。

分析问题空间

让我们看看你在评论中所写的内容:

  

我在其他帖子下读过与你类似的答案,但我的意思是   例如,测试我的错误。我有一些用户填充的对象   只有部分,系统自动填充其他字段后,   对于前创建日期,创建用户等。当我想念填充一些   这些字段SaveChanges()会给我一个错误。

您正在从错误的角度接近此任务。

我为什么这么说?这是因为:

  1. 您正在使用实体框架ORM(EFpersistence model作为行为的来源,该模型负责指定的业务交互

  2. 您希望EF进行此类验证

  3. 您想通过EF机制测试所有这些。你正在测试错误的东西

  4. 解决问题

    你真正想要做的就是将你的模型融入EF的核心。哪个不好因为:

    • 您将代码紧密耦合到EF,并具有不必要的依赖性
    • 它使您的业务逻辑测试变得困难和缓慢
    • 业务逻辑是代码中最重要和最有价值的部分之一,您(在理想条件下)最终会获得报酬

    现在让我们重点关注上面的前三点。

    首先:我强烈建议您创建一个可能更少依赖的对象。为了示例,我们将其称为实体,它将包含所有必需的行为封装。就像你提到的那样;有公共方法来设置属性和其他不变量。

    第二:您还可以拥有保护此类型及其所有不变量所需的所有验证。这种验证的常见位置可以是构造函数或接受和验证它接收的参数的任何公共方法。如果出现错误,您可以抛出自定义业务异常并在测试中断言这些异常。

    这些全部组合成一个对象,被称为; cohesion

    第三:现在你有一个更清洁的对象,它为给定的业务交互建模,你现在只需要完全隔离地测试这个代码。这是一件好事,因为它速度快,重点突出,并且不会加载大量的依赖项(与使用EF进行集成测试相比)。

    一切顺利

    当然,与所有事情一样,所有这一切都需要付出代价。那就是当你将某些东西与系统分开时,你可能会引入另一层间接。这就是你现在需要映射你的&#34;域模型&#34;到EF持久性模型,反之亦然。