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

C# EF Moq单元测试,不确定验证,c#,entity-framework,unit-testing,moq,C#,Entity Framework,Unit Testing,Moq,我对单元测试完全陌生。我从不同的人那里读了很多“教程”,我决定使用msdn解决方案 我将此用作测试,我对“测试非查询场景”感兴趣 根据本文,我尝试测试我的简单CRUD的Create()操作 这是我的代码(FinancialAsistanties是我的DbContext(EF数据库优先)): 上下文: public partial class FinancialAssistantEntities : DbContext { public FinancialAssistantEntities

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

我将此用作测试,我对“测试非查询场景”感兴趣

根据本文,我尝试测试我的简单CRUD的Create()操作

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

上下文:

public partial class FinancialAssistantEntities : DbContext
{
    public FinancialAssistantEntities()
        : base("name=FinancialAssistantEntities")
    {
    }
    .
    .
    .
    public virtual DbSet<FAWallet> FAWallet { get; set; }
}
[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()); 
}
public部分类金融系统:DbContext
{
公共财政系统()
:base(“名称=金融系统”)
{
}
.
.
.
公共虚拟数据库集{get;set;}
}
存储库方法:(我用注释掉了我事务的,因为从测试方法运行它会导致错误“在应用程序配置文件中找不到名为'FinancialAsistanteties'的连接字符串”。)

公共异步任务CreateWallet(FAWallet模型)
{
使用(var context=context)
{
//具有IsolationLevel的事务
//使用(var-tran=context.Database.BeginTransaction(IsolationLevel.ReadUncommitted))
{
尝试
{                       
context.FAWallet.Add(model);
//SaveChanges();
wait context.saveChangesSync();
//trans.Commit();
返回true;
}
捕获(例外情况除外)
{
//事务回滚();
掷骰子;
}
}
}
}
测试方法:

public partial class FinancialAssistantEntities : DbContext
{
    public FinancialAssistantEntities()
        : base("name=FinancialAssistantEntities")
    {
    }
    .
    .
    .
    public virtual DbSet<FAWallet> FAWallet { get; set; }
}
[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()); 
}
[TestMethod]
公共无效CreateWalletTest()
{
var wallet=新的FAWallet()
{
WalletId=Guid.NewGuid(),
//WalletName=StringHelper.GeneratorDomainString(12),
//管理员ID
WalletUserId=“e6888245-1d9b-431c-a068-aa62932e47ec”,
WalletCreateDate=日期时间。现在,
WalletEnabled=真
};
var mockSet=new Mock();
var mockContext=new Mock();
mockContext.Setup(x=>x.FAWallet).Returns(mockSet.Object);
var walletRepository=新的FAWalletRepository(mockContext.Object);
walletRepository.CreateWallet(wallet.Wait();
验证(x=>x.Add(It.IsAny()),Times.Once());
验证(x=>x.saveChangesSync(),Times.Once());
}
首先,我不知道使用
out注释事务是否是个好主意,尽管我对测试知之甚少

第二,我的考试总是通过。我甚至注释掉了WalletName属性的集合,因为该字段不能为空,所以似乎我做错了什么。

前言 在我们开始检查您的问题之前,让我明确一点,主要问题不是单元测试。取而代之的,是关于和一些分析

问题空间分析 让我们看看你在评论中写了什么:

我在其他帖子中也读过类似的答案,但我的意思是 例如,测试我的错误。我有一些由用户填充的对象 只有部分,在系统自动填充其他字段之后, 例如创建日期、创建用户等,当我错过填写一些 这些字段,
SaveChanges()
将向我抛出一个错误

您从错误的角度处理此任务

我为什么这么说?因为:

  • 您正在使用实体框架ORM()作为行为源,该模型负责给定的业务交互

  • 您希望EF进行这种验证吗

  • 您希望通过EF机制测试所有这些。你在测试错误的东西

  • 解决问题 你真正想做的是把你的模型和EF的核心联系起来。这是不好的,因为:

    • 您将代码与EF紧密耦合,并带有不必要的依赖项
    • 这使得测试业务逻辑变得既困难又缓慢
    • 业务逻辑是代码中最重要和最有价值的部分之一,在理想的情况下,您最终会得到报酬
    现在让我们详细关注上面的前三点

    首先:我强烈建议您创建一个依赖关系越少的对象。为了这个示例,让我们称它为实体,它将包含所有需要封装的行为。就像你提到的;使用公共方法设置属性和其他不变量

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

    所有这些组合成一个对象,称为

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


    结局好,一切都好
    当然,这一切都是有代价的。也就是说,当你把某些东西从系统中分离出来的时候,你可能会引入另一个间接层。这就是您现在需要将“域模型”映射到EF持久性模型,反之亦然。

    您基本上是在尝试测试EF是否正在执行其设计目的。不要那样做。微软会在发布使用之前对其进行全面测试。您可以考虑抽象化对上下文的依赖性。这个特定的模拟框架使用虚拟方法工作,这就是为什么您无法模拟代码的事务部分。这