【问题标题】:Perform xUnit Moq test to create record and save it to DbContext执行 xUnit Moq 测试以创建记录并将其保存到 DbContext
【发布时间】:2021-01-24 05:55:09
【问题描述】:

我正在为用 .NET CORE 3.1 编写的 Web API 应用程序编写测试。我正在使用 xUnit、AutoFixture 和 Moq 进行测试。我有一个使用实体框架/ DbContext 在数据库中创建新学校实例的类。我的问题是如何模拟 dbContext 并保存更改,此外,我的 School DataModel 有一个:与 SchoolBranch DataModel 的许多关系。我已经按照这个教程https://docs.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking

错误

Message: 
Moq.MockException : 
Expected invocation on the mock once, but was 0 times: m => m.Add<School>(It.IsAny<School>())

Performed invocations:

   Mock<SchoolDbContext:1> (m):
   No invocations performed.

 Stack Trace: 
   Mock.Verify(Mock mock, LambdaExpression expression, Times times, String failMessage)
   Mock`1.Verify[TResult](Expression`1 expression, Times times)
   CreateSchoolCommandTest.ExecuteMethod_ShouldReturnNewGuidId_IfSuccess() line 50

学校

 public class School
{
    public School()
    {
        this.SchoolBranches = new HashSet<SchoolBranch>();
    }

    public Guid SchoolID { get; set; }
    public string Name { get; set; }

    public ICollection<SchoolBranch> SchoolBranches { get; set; }
}

学部

public class SchoolBranch
{
    public SchoolBranch()
    {
    }

    public Guid SchoolBranchID { get; set; }
    public Guid SchoolID { get; set; }
    public string Address { get; set; }
    public int PhoneNumber { get; set; }

    public School School { get; set; }
}

创建学校类

public class CreateSchool : BaseCommand<Guid>, ICreateSchool
{
    public SchoolDto SchoolDtos { get; set; }

    public CreateSchool(IAppAmbientState appAmbient) : base(appAmbient) { }
   
    public override Guid Execute()
    {
        try
        {
            var schoolId = Guid.NewGuid();
            List<SchoolBranch> schoolBranches = new List<SchoolBranch>();

            foreach(var item in SchoolDtos.SchoolBranchDtos)
            {
                schoolBranches.Add(new SchoolBranch()
                {
                    SchoolBranchID = Guid.NewGuid(),
                    SchoolID = schoolId,
                    Address = item.Address,
                    PhoneNumber = item.PhoneNumber
                });
            }

            var school = new School()
             {
                 SchoolID = schoolId,
                 Name = SchoolDtos.Name,
                 SchoolBranches = schoolBranches
             };

            schoolDbContext.Schools.Add(school);
            schoolDbContext.SaveChanges();

            return school.SchoolID;

        }
        catch(Exception exp)
        {
            appAmbientState.Logger.LogError(exp);
            throw;
        }
    }
}

测试类

 public class CreateSchoolCommandTest
 {
    private readonly ICreateSchool sut;
     private readonly Mock<IAppAmbientState> appAmbientState = new Mock<IAppAmbientState>();


 [Fact]
    public void ExecuteMethod_ShouldReturnNewGuidId_IfSuccess()
    {
        //Arrange
        var fixture = new Fixture();
        var schoolDtoMock = fixture.Create<SchoolDto>();

        var schoolDbSetMock = new Mock<DbSet<School>>();
        var schoolBranchDbSetMock = new Mock<DbSet<SchoolBranch>>();

        var schoolDbContextMock = new Mock<SchoolDbContext>();

        //schoolDbSetMock.Setup(x => x.Add(It.IsAny<School>())).Returns((School s) => s); // this also did not work
        

        schoolDbContextMock.Setup(m => m.Schools).Returns(schoolDbSetMock.Object);
        
        //Act
        sut.SchoolDtos = schoolDtoMock;
        var actualDataResult = sut.Execute();


        // Assert
        Assert.IsType<Guid>(actualDataResult);
        schoolDbContextMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
        schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
    } 

BaseCommand(这里创建了DbContext)

public abstract class BaseCommand<T>
{
    protected SchoolDbContext schoolDbContext;
    protected IAppAmbientState appAmbientState { get; }

    public BaseCommand(IAppAmbientState ambientState)
    {
        this.schoolDbContext = new SchoolDbContext();
        this.appAmbientState = ambientState;
    }

    public abstract T Execute();
}

【问题讨论】:

    标签: unit-testing moq xunit .net-core-3.1 autofixture


    【解决方案1】:

    修复错误

    你只是犯了一个小错误。安装的

    schoolDbContextMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
    schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
    

    你应该有

    schoolDbSetMock.Verify(m => m.Add(It.IsAny<School>()), Times.Once());
    schoolDbContextMock.Verify(m => m.SaveChanges(), Times.Once());
    

    因为您在schoolDbContext.Schools 上使用方法Add() 而不是schoolDbContext

    用于注入 dbContext

    您的 BaseCoommand 类构造函数应如下所示:

    public BaseCommand(IAppAmbientState ambientState, SchoolDbContext schoolDbContext)
    {
        this.schoolDbContext = schoolDbContext;
        this.appAmbientState = ambientState;
    }
    

    您的 CreateSchool 类构造函数:

    public CreateSchool(IAppAmbientState appAmbient, SchoolDbContext schoolDbContext) : base(appAmbient, schoolDbContext) { }
    

    接下来在测试中你应该像这样在测试中初始化CreateSchool

    var sut = new CreateSchool(ambientState, schoolDbContextMock.Object);
    

    它会起作用的

    【讨论】:

    • 谢谢,沃伊切赫。我已经按照您的描述更新了代码,但仍然出现以下错误............预期在模拟上调用一次,但为 0 次:m => m.Add(It.IsAny())
    • 告诉我你是如何在CreateSchool类中创建schoolDbContext的。如果你用构造函数注入它,在测试中你需要像这样new CreateSchool(schoolDbContextMock.Object) 调用smth。如果可以的话,给我链接到回购,或者整个代码,这样我就可以帮助你
    • DbContext 'SchoolDbContext' 是在抽象类 'BaseCommand' 中创建的。我已将代码粘贴在最后的问题中
    • 我编辑了答案并举例说明了 DBcontext 注入。在这里您可以阅读有关创建 DBContext 对象的更多信息:docs.microsoft.com/pl-pl/ef/core/miscellaneous/… 基本上是关于在 Startup.cs 中添加一行 services.AddDbContext&lt;&gt;(your configuration);
    • 有什么问题就写吧
    猜你喜欢
    • 2019-08-10
    • 2020-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-02
    • 2018-01-26
    • 2014-01-31
    • 1970-01-01
    相关资源
    最近更新 更多