执行xUnit Moq测试以创建记录并将其保存到DbContext

问题描述

我正在编写用.NET CORE 3.1编写的Web API应用程序的测试。我正在使用xUnit,AutoFixture和Moq进行测试。我有一个使用Entity Framework / DbContext在数据库中创建新学校实例的类。我的问题是如何模拟dbContext并保存更改,此外我的School DataModel还有一个:与SchoolBranch DataModel的许多关系。我已遵循本教程http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm

错误

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

SchoolBranch

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

CreateSchool类

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

解决方法

针对修复错误

您犯了一个小错误。插入

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());

因为您在Add()而不是schoolDbContext.Schools上使用方法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);

它将起作用

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...