Moq:使用模拟参数设置通用方法

问题描述 投票:0回答:1

我一直试图在NUnit中为我的通用方法编写一些测试,但没有成功。我希望我能说清楚我的情况,因为我必须大量解释我的代码。

DoBusinessLogic()是我想要测试的方法。它从基类调用另外两个方法:

public class MySvc : BaseSvc, IMySvc
{
    private readonly IMyRepo _repo;
    private readonly IMyConnectorClass _connector
    public MySvc(IMyRepo repo) : base(repo)        
    {
        _repo = repo;
        _connector = _repo.GetConnector();
    }

    public async Task DoBusinessLogic(int id1, int id2){
        bool doesFirstObjectExist = await CheckMainObjExists<Foo>(id1, _connector);
        await CheckSubObjExists<Bar>(id2, _connector);
        // further business logic
    }
}

反过来,这些方法调用相同的存储库方法,但在它之后具有不同的逻辑:

public abstract class BaseSvc : IBaseSvc
{
    private readonly IBaseRepo _repo
    protected BaseSvc(IBaseRepo repo)
    {
        _repo = repo;
    }

    protected async Task<bool> CheckMainObjExists<T>(int? id, IMyConnectorClass connector)
    {
        return await _repo.GetObjectByIdAsync<T>(Id, connector) != null;
    }

    protected async Task CheckSubObjExists<T>(int? id, IMyConnectorClass connector)
    {
        if (await _repo.GetObjectByIdAsync<T>(Id, connector) == null)
            { throw new Exception("Object not found!"); }
    }
}

接下来,我想在MySvc类中为DoBusinessLogic()编写单元测试。不幸的是,我似乎无法模拟存储库中的响应。

[TestFixture]
public class MySvcTests
{
    private MySvc _svc;
    private Mock<IMyRepo> _repoMock;
    private Mock<IMyConnectorClass> _connectorMock;

    [SetUp]
    public void SetUp()
    {
        _repoMock = new Mock<IMyRepo>() {};
        _connectorMock = new Mock<IMyConnectorClass>();

        _repo.SetUp(r => r.GetConnector()).Return(_connectorMock.Object);

        _svc = new MySvc(_repoMock);    
    }

    /* 
    My intent in this test, is to make CheckMainObjExists() pass,
    but make CheckSubObjExist() fail.
    */
    [Test]
    public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException()
    {
        // This should return an object
        _repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
            .ReturnsAsync(new Foo());

        // This should return null
        _repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
            .ReturnsAsync((Bar) null);

        Assert.Throws<Exception>(await _svc.DoBusinessLogic());
    }
}

但是,当我运行测试时,我为我的模拟仓库设置的两个方法都返回null,而我期望从第一个开始“返回”。我不知道问题出在哪里,但我怀疑:

  1. 是否可以使用模拟对象作为参数来设置方法?在这种情况下,是否可以使用_connectorMock.Object作为设置参数?
  2. 是否可以多次设置相同的通用方法,但每次都使用不同的类型?这是Foo的首次设置,然后是Bar。
c# unit-testing nunit moq
1个回答
0
投票

我刚测试了这段代码,它按预期运行。现在我不得不做出很多假设只是为了让代码编译和运行,这意味着我对你的代码的测试是有缺陷的,因为我可能修复了你的例子中省略的东西。

我没有对您的测试设置代码进行任何更改。

[TestClass]
public class MySvcTests {
    [TestMethod]
    [ExpectedException(typeof(Exception))]
    public async Task DoBusinessLogic_If2ndObjectNotExist_ThrowException() {
        var _repoMock = new Mock<IMyRepo>() { };
        var _connectorMock = new Mock<IMyConnectorClass>();

        _repoMock.Setup(r => r.GetConnector()).Returns(_connectorMock.Object);

        var _svc = new MySvc(_repoMock.Object);
        // This should return an object
        _repoMock.Setup(r => r.GetObjectByIdAsync<Foo>(It.IsAny<int>(), _connectorMock.Object))
            .ReturnsAsync(new Foo());

        // This should return null
        _repoMock.Setup(r => r.GetObjectByIdAsync<Bar>(It.IsAny<int>(), _connectorMock.Object))
            .ReturnsAsync((Bar)null);

        await _svc.DoBusinessLogic(0, 0);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.