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

C# Moq:使用模拟参数设置通用方法,c#,unit-testing,nunit,moq,C#,Unit Testing,Nunit,Moq,我一直试图用NUnit为我的泛型方法编写一些测试,但没有成功。我希望我能把我的情况说清楚,因为我不得不大量地解释我的代码 DoBusinessLogic()是我要测试的方法。它从基类调用另外两个方法: public class MySvc : BaseSvc, IMySvc { private readonly IMyRepo _repo; private readonly IMyConnectorClass _connector public MySvc(IMyRepo

我一直试图用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
    }
}
公共类MySvc:BaseSvc,IMySvc { 私人只读IMyRepo(u repo),; 专用只读IMyConnectorClass\u连接器 公共MySvc(IMyRepo回购):基础(回购) { _回购=回购; _连接器=_repo.GetConnector(); } 公共异步任务DoBusinessLogic(int id1,int id2){ bool doesFirstObjectExist=wait CheckMainObjExists(id1,_连接器); 等待检查是否存在(id2,_连接器); //进一步的业务逻辑 } } 这些方法依次调用相同的存储库方法,但其后面有不同的逻辑:

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!"); }
    }
}
公共抽象类BaseSvc:IBaseSvc
{
私有只读IBaseRepo\u repo
受保护的BaseSvc(IBaseRepo回购)
{
_回购=回购;
}
受保护的异步任务CheckMainObjExists(int?id,IMyConnectorClass连接器)
{
return wait _repo.GetObjectByIdAsync(Id,连接器)!=null;
}
受保护的异步任务检查子对象存在(int?id,IMyConnectorClass连接器)
{
if(wait _repo.GetObjectByIdAsync(Id,连接器)==null)
{抛出新异常(“找不到对象!”;}
}
}
接下来,我想在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());
    }
}
[TestFixture]
公共类测试
{
私人MySvc_svc;
私人模拟(repoMock),;
专用模拟(u connectorMock),;
[设置]
公共作废设置()
{
_repoMock=newmock(){};
_connectorMock=newmock();
_repo.SetUp(r=>r.GetConnector()).Return(_connectorMock.Object);
_svc=新的MySvc(_repoMock);
}
/* 
我在这个测试中的目的是让CheckMainObjExists()通过,
但要使checkSubbjectExist()失败。
*/
[测试]
公共异步任务DoBusinessLogic_If2ndObjectNotExist_ThroweException()
{
//这应该返回一个对象
_repoMock.Setup(r=>r.GetObjectByIdAsync(It.IsAny(),_connectorMock.Object))
.ReturnsAsync(新Foo());
//这应该返回null
_repoMock.Setup(r=>r.GetObjectByIdAsync(It.IsAny(),_connectorMock.Object))
.ReturnsAsync((Bar)null);
Assert.Throws(wait _svc.DoBusinessLogic());
}
}
然而,当我运行测试时,我为模拟回购设置的两个方法都返回null,而我期望第一个方法返回“true”。 我不知道问题出在哪里,但我怀疑:

  • 是否可以使用模拟对象作为参数来设置方法?在这种情况下,是否可以使用_connectorMock.Object作为设置参数
  • 是否可以多次设置相同的泛型方法,但每次设置的类型不同?首先是Foo的设置,然后是Bar的设置

  • 我刚刚测试了这段代码,它按预期运行。现在我不得不做很多假设,只是为了让代码编译和运行,这意味着我对您的代码的测试有缺陷,因为我可能已经修复了您的示例中遗漏的某些内容

    我没有对您的测试设置代码进行任何更改,这很有效

    [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);
        }
    }
    
    [TestClass]
    公共类测试{
    [测试方法]
    [ExpectedException(typeof(Exception))]
    公共异步任务DoBusinessLogic_If2ndObjectNotExist_ThroweException(){
    var_repoMock=new Mock(){};
    var_connectorMock=new Mock();
    _repoMock.Setup(r=>r.GetConnector())。返回(_connectorMock.Object);
    var _svc=newmysvc(_repoMock.Object);
    //这应该返回一个对象
    _repoMock.Setup(r=>r.GetObjectByIdAsync(It.IsAny(),_connectorMock.Object))
    .ReturnsAsync(新Foo());
    //这应该返回null
    _repoMock.Setup(r=>r.GetObjectByIdAsync(It.IsAny(),_connectorMock.Object))
    .ReturnsAsync((Bar)null);
    wait_svc.DoBusinessLogic(0,0);
    }
    }
    
    如何将模拟传递给被测试的类?没有看到任何注入点。@Nkosi通过IoC。我更新了帖子以便澄清。无论是
    CheckMainObjExists
    还是
    checksubbjexists
    都不使用传递的
    id
    参数。示例中的输入错误或真实问题?@AdsNobrega您需要提供一个帮助我们了解问题所在的示例。您删除了太多的细节,测试与正在测试的代码不匹配。@Nkosi,我深表歉意。我已经完成了我帖子中的代码,我希望它现在更清晰。我问题的主要焦点是下面的_repoMock方法的设置。