简体   繁体   English

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

[英]EF Moq Unit Test, unsure verify

I am totally new to unit tests. 我对单元测试完全不熟悉。 I read many "tutorials" from different people and I decided to use msdn solutions. 我从不同的人那里读了许多“教程”,我决定使用msdn解决方案。

I use this https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx as my test, I am interested in " Testing non-query scenarios " there. 我使用这个https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx作为我的测试,我感兴趣的是“ 测试非查询场景 ”。

According to this article, I tried to test my simple CRUD's Create() action. 根据这篇文章,我试着测试我简单的CRUD的Create()动作。

Here's my code ( FinancialAssistantEntities is my DbContext (EF Database First)): 这是我的代码( FinancialAssistantEntities是我的DbContext (EF数据库优先)):

Context: 语境:

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

Repository method: (I commented out my transaction's using , because running it from test method causes the error "No connection string named 'FinancialAssistantEntities' could be found in the application config file."), 存储库方法:(我注释掉了我的事务的使用 ,因为从测试方法运行它会导致错误“在应用程序配置文件中找不到名为'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()); 
}

First of all, I don't know if commenting transaction's using out is good idea, though I don't know much about testing yet. 首先,我不知道评论交易的使用是否是个好主意,尽管我对测试还不太了解。

Secondly, my test always passes. 其次,我的测试总是过去。 I even commented out the WalletName property's set, since this field is not-nullable, so it seems I made sth wrong. 我甚至注释掉了WalletName属性的集合,因为这个字段不可为空,所以看起来我做错了。

Foreword 前言

Before we start off with examining the bits and pieces of your question, let me be clear that the main problem is not about unit testing. 在我们开始检查您的问题的各个部分之前,让我明确一下,主要问题不在于单元测试。 Instead, it's about Object Oriented Programming and some of analysis. 相反,它是关于面向对象编程和一些分析。

Analyzing the problem space 分析问题空间

Let's see what you wrote in a comment: 让我们看看你在评论中写的内容:

I've read similar answers like yours under other threads, but I mean testing my mistakes, for example. 我在其他线程下读过像你这样的类似答案,但我的意思是测试我的错误。 I have some objects filled by user only partially, after that system automatically fills other fields, for ex. 我有一些用户仅部分填充的对象,之后系统自动填充其他字段,例如。 create date, create user, etc. When I miss filling some of these fields, SaveChanges() would throw me an error. 创建日期,创建用户等。当我想念填充其中一些字段时, SaveChanges()会给我一个错误。

You are approaching this task from the wrong angle. 您正在从错误的角度接近此任务。

Why do I say this? 我为什么这么说? Because: 因为:

  1. You are using Entity Framework ORM ( EF ) persistence model as the source of behavior , the model held responsible for the given business interaction 您正在使用Entity Framework ORM( EF持久性模型作为行为的来源,该模型负责给定的业务交互

  2. You want EF to this kind of validation 您希望EF进行此类验证

  3. You want to test all this through EF mechanism. 您想通过EF机制测试所有这些。 You are testing the wrong thing 你正在测试错误的东西

Addressing the issues 解决问题

What you really want to do is tie your model into EF in its very heart. 你真正想要做的就是将你的模型融入EF的核心。 Which is not good because: 哪个不好因为:

  • You are tightly coupling your code to EF, with unnecessary dependencies 您将代码紧密地耦合到EF,具有不必要的依赖性
  • It makes testing your business logic hard and slow 它使您的业务逻辑测试变得困难和缓慢
  • The business logic is one of most important and valuable parts of your code, something you (in ideal conditions) get paid for in the end 业务逻辑是代码中最重要和最有价值的部分之一,您(在理想条件下)最终会获得报酬

Now let's focus on the first three points above in detail. 现在让我们重点关注上面的前三点。

First: I'm strongly advising you to create an object which has the more less dependencies possible. 第一:我强烈建议你创建一个可能更少依赖的对象。 Let's call it an entity for the sake of the example, which will contain all the needed behavior encapsulated. 为了示例,我们将其称为实体 ,它将包含封装的所有必需行为 Like you have mentioned; 就像你提到的那样; have public methods to set properties and other invariants. 有公共方法来设置属性和其他不变量。

Second: You can also have all the validations needed to guard this type and all of its invariants. 第二:您还可以拥有保护此类型及其所有不变量所需的所有验证。 The common place for such validation can be the constructor or any public method accepting and validating arguments it receives. 这种验证的常见位置可以是构造函数或接受和验证它接收的参数的任何公共方法。 In case of errors you can just throw your custom business exceptions and assert against these in your tests. 如果出现错误,您可以在测试中抛出自定义业务异常并对其进行断言。

These all combined, into one object, is called; 这些全部组合成一个对象,被称为; cohesion . 凝聚力

Third: now that you have a cleaner object, which models the given business interaction, you now just need to test this code in total isolation. 第三:既然你有一个更清洁的对象,它模拟给定的业务交互,你现在只需要完全隔离地测试这个代码。 Which is a nice thing to have, because it's fast, it's focused and does not load hordes of dependencies (compared to integration testing with EF). 这是一件好事,因为它很快,它集中并且不会加载大量的依赖项(与使用EF的集成测试相比)。


All's Well That Ends Well 一切都很顺利

Of course like with everything, all this comes with a price. 当然,就像所有东西一样,这一切都需要付出代价。 And that is when you separate something from the system you might be introducing another layer of indirection. 那就是当你将某些东西与系统分开时,你可能会引入另一层间接。 And this is that you now need to map your "domain model" to an EF persistence model and vice versa. 这就是您现在需要将“域模型”映射到EF持久性模型,反之亦然。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM