代码之家  ›  专栏  ›  技术社区  ›  Alex

从实际对象的实例返回默认模拟结果

  •  0
  • Alex  · 技术社区  · 2 年前

    考虑一个需要编写的场景 integration tests 对于 REST API 控制器。我用 MSTest 为了这个。逻辑如下:

    1. 创造新的 database ,运行init SQL 迁徙
    2. 创建测试 HTTP Client 打电话反对 endpoints
    3. DB .

    用例 大多数端点都进行验证,无论 SQL 命令( INSERT , UPDATE , DELETE )返回了一个成功结果(布尔值) true )或者不是。要复制这个场景,我需要知道 SQL 故障条件(硬),或者更简单的选择是 能够模拟存储库 .当然,这不再是一个完整的集成测试,但至少我能够进行测试 REST 这样的商业逻辑 HTTP response status codes 。另外一个好处是测试DB事务回滚功能。

    问题 因此,我想知道——是否有可能创建一个模拟,默认情况下会返回实际对象实例的所有值,除非 setup() 是否提供了覆盖?

    示例/工作代码 在测试初始化时,我们创建一个HTTP测试客户端实例和一个模拟存储库:

    _usersRepo = new UsersRepository(); // actual instance of repository
    
    var application = new WebApplicationFactory<Startup>()
    .WithWebHostBuilder(builder =>
    {
        builder.ConfigureTestServices(services =>
        {
            _usersRepoMock = new Mock<IUsersRepository>();
    
            // setting mock just for 1 of the methods manually
            _usersRepoMock
                .Setup(p => p.GetByIdAsync(It.IsAny<long>()))
                .Returns(async (long r) => await _usersRepo.GetByIdAsync(r));
    
            services.AddSingleton<IUsersRepository>(_usersRepoMock.Object);
        });
    });
    _client = application.CreateClient(new WebApplicationFactoryClientOptions());
    

    我已经手动配置了一个模拟来返回结果 从实际的存储库 对于单一方法。

    然后,在测试课上,每当我需要一些不及格的东西时,我都会做以下事情:

    [TestMethod]
    public async Task Db_throws_exception()
    {
        _usersRepoMock.Setup(p => p.GetByIdAsync(It.IsAny<long>())).Throws(new System.Exception("test"));
        //.. rest of the code
    }
    

    这个解决方案正如预期的那样工作,然而,问题是为了让这种方法工作,我需要手动将所有模拟对象请求代理到实际实例。我在每次回购中有大约4-6种方法,以及大约8种不同的回购(未来会有更多) 我想避免这样做 .可能吗?

    以下是我想避免做的事情:

    _usersRepoMock = new Mock<IUsersRepository>();
    _usersRepoMock.Setup(p => p.GetByIdAsync(It.IsAny<long>())).Returns(async (long r) => await _usersRepo.GetByIdAsync(r));
    _usersRepoMock.Setup(p => p.Create(It.IsAny<long>())).Returns(async (long r) => await _usersRepo.Create(r));
    _usersRepoMock.Setup(p => p.Update(It.IsAny<long>())).Returns(async (long r) => await _usersRepo.Update(r));
        
    _repo2Mock = new Mock<IRepo2>();
    _repo2Mock.Setup(p => p.Method1(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method1(r));
    _repo2Mock.Setup(p => p.Method2(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method2(r));
    _repo2Mock.Setup(p => p.Method3(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method3(r));
    _repo2Mock.Setup(p => p.Method4(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method4(r));
    _repo2Mock.Setup(p => p.Method5(It.IsAny<long>())).Returns(async (long r) => await _repo2.Method5(r));
    
    _repo3Mock = new Mock<IRepo3>();
    _repo3Mock.Setup(p => p.Method1(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method1(r));
    _repo3Mock.Setup(p => p.Method2(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method2(r));
    _repo3Mock.Setup(p => p.Method3(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method3(r));
    _repo3Mock.Setup(p => p.Method4(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method4(r));
    _repo3Mock.Setup(p => p.Method5(It.IsAny<long>())).Returns(async (long r) => await _repo3.Method5(r));
    

    关于stackoverflow有一些类似的问题,但我找不到与我相同的用例,也找不到一个可接受的问题答案。。以下是一个例子: Default behavior for mock inherited from object instance

    p、 我写这个问题的时间可能比写模拟代码的时间要多:)

    0 回复  |  直到 2 年前