【问题标题】:Mock of DbSet AddOrUpdate fails to update my objectDbSet AddOrUpdate 的模拟无法更新我的对象
【发布时间】:2019-01-12 02:03:58
【问题描述】:

我有这个单元测试。它为我的IEmployeeService 测试UpdateEmployee 方法。

    private readonly IEmployeeService _employeeService;

    public EmployeeServiceTests()
    {
        var mockData = new List<Employee>
        {
            new Employee { Id = 0, FirstName = "Homer", LastName = "Simpson" },
            new Employee { Id = 1, FirstName = "Carl", LastName = "Carlson" },
            new Employee { Id = 2, FirstName = "Lenny", LastName = "Leonard" },
        };

        _employeeService = MockSetup.SetupEmployeeService(mockData);
    }

    [Fact]
    public void UpdateEmployee_EmployeeExists_EmployeeGetsUpdated()
    {
        var homer = _employeeService.GetEmployee(0);
        homer.FirstName = "Homer Jay";

        _employeeService.UpdateEmployee(homer);

        var actual = homer.FirstName;
        var expected = _employeeService.GetEmployee(0).FirstName;

        Assert.Equal(expected, actual);
    }

这是我的UpdateEmployee() 代码:

    public void UpdateEmployee(EmployeeDto employee)
    {
        var existingEmployee = _dbContext.Employees.SingleOrDefault(e => e.Id == employee.Id);

        if (existingEmployee != null)
        {
            _dbContext.Employees.AddOrUpdate(employee.ToEntity());
        }
        else
        {
            throw new Exception("Employee does not exist");
        }
    }

这就是我的模拟设置的样子。我使用自定义方法来模拟AddOrUpdate

    public static Mock<MockDbSet<Employee>> CreateEmployeeMockSet(List<Employee> data)
    {
        var mockSet = new Mock<MockDbSet<Employee>>();
        mockSet.As<IQueryable<Employee>>().Setup(m => m.Provider).Returns(data.AsQueryable().Provider);
        mockSet.As<IQueryable<Employee>>().Setup(m => m.Expression).Returns(data.AsQueryable().Expression);
        mockSet.As<IQueryable<Employee>>().Setup(m => m.ElementType).Returns(data.AsQueryable().ElementType);
        mockSet.As<IQueryable<Employee>>().Setup(m => m.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());
        mockSet.Setup(d => d.Add(It.IsAny<Employee>())).Callback<Employee>(e => data.Add(e));
        mockSet.Setup(d => d.AddOrUpdate(It.IsAny<Employee>())).Callback<Employee>(e => UpdateList(e, data));
        mockSet.Setup(d => d.Remove(It.IsAny<Employee>())).Callback<Employee>(e => data.Remove(e));

        return mockSet;
    }

    private static void UpdateList(Employee employee, List<Employee> data)
    {
        var index = data.FindIndex(e => e.Id == employee.Id);

        data[index] = employee;
    }

很遗憾,更新不起作用。我收到此错误:

Message: Assert.Equal() Failure
               ↓ (pos 5)
Expected: Homer
Actual:   Homer Jay
               ↑ (pos 5)

我哪里错了?

【问题讨论】:

  • 您的UpdateEmployee 方法在代码中的什么位置?
  • @TanvirArjel 我编辑了问题,对此感到抱歉
  • 这是一个 ASP.NET Core 项目吗?
  • @TanvirArjel 这是.net framework 4.6.1

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


【解决方案1】:

这看起来像是你正在测试你的模拟,这太深了。模拟是边界。它应该断言与模拟服务的交互,而不是替代它。

首先是一个警告标志:AddOrUpdate 用于数据迁移,而不是生产代码。如果您将不完整的实体传递给它,则可以引入副作用。 (删除数据)

例如,如果我要模拟一个 DbSet 或存储库,我会考虑:

  • 模拟 DbSet/Repo,它为我的测试场景返回一个已知状态。 (员工,什么都没有,例外)
  • 断言是否调用了 SaveChanges? (是的,当我让员工回来时,如果在获取期间没有员工返回或异常情况,我的服务是否适当地调用了 SaveChanges?)
  • 如果 SaveChanges 引发异常,则断言该行为。 (我的服务应该做什么?)

从 mock 中获取对象,然后测试该 mock 是否记录了更新是在测试 mock,而不是应该测试的服务代码。

【讨论】:

  • 感谢您提供的信息。一个问题:你说我不应该在生产中使用 AddOrUpdate。那我应该用什么?
  • 您的应用程序应该跟踪您是在处理新实体场景还是更新现有实体场景,并分别处理这些场景。在更新的情况下,它还应该考虑一个行版本(时间戳等)来检测陈旧数据的变化。 (数据由另一个用户/进程更新,因为此更新已“读取”) Re:AddOrUpdate 的风险:michaelgmccarthy.com/2016/08/24/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-08
  • 2015-05-11
相关资源
最近更新 更多