C# 如何使用未传递给真实对象的模拟

C# 如何使用未传递给真实对象的模拟,c#,nunit,C#,Nunit,我有如下所示的服务。它有我想测试的Add方法。更详细地说,我想检查是否会到达ADDCANK INDED public class CarTankService : ICarTankService { private readonly ITankQuery _tankQuery; private readonly CarClient _carClient; public CarTankService(ITankQuery tankQuery) {

我有如下所示的服务。它有我想测试的Add方法。更详细地说,我想检查是否会到达ADDCANK INDED

public class CarTankService : ICarTankService
{
    private readonly ITankQuery _tankQuery;
    private readonly CarClient _carClient;

    public CarTankService(ITankQuery tankQuery)
    {
        _tankQuery = tankQuery;
        _carClient = new CarClient();
    }

    public ObservableCollection<CarTank> GetTanks() => _carClient.Tanks;

    public void GenerateNewList() => _carClient.GenerateNewTanksList();

    public virtual void Add(CarTank tank)
    {
        if (_tankQuery.isExist(tank.Number)) throw new OwnException()

        _carClient.AddTank(tank);
    }

    public virtual void Remove(CarTank tank) => _carClient.RemoveCarTank(tank);
}
这是我的测试方法类:

[TestFixture]
class CarTankServiceTests
{
    private Mock<ITankQuery> TankQuery { get; set; }
    private ICarTankService CarTankService { get; set; }
    private Mock<CarClient> CarClient { get; set; }

    [SetUp]
    public void SetUp()
    {
        TankQuery = new Mock<ITankQuery>();
        CarClient = new Mock<CarClient>();
        CarTankService = new CarTankService(TankQuery.Object);
    }

    [Test]
    public void Add_NotExistReferenceNumber_AddTankReached()
    {
        TankQuery.Setup(uow => uow.isExist(It.IsAny<int>())).Returns(false);

        CarTankService.Add(new CarTank());

        CarClient.Verify(uow => uow.AddTank(It.IsAny<ClientTank>()),Times.Once);
    }
}
CarClient.Verify for AddTank始终显示测试中出现0,在本例中为非真。我不确定,但我认为这是因为CarClient模型类,因为它不是直接注入到我的服务中,它总是显示0。我说得对吗?有没有测试它的选项?

如果你模拟你的CarClient,你必须在这里设置所有你想在测试中使用的方法。在您的代码中,我们有两个CarClient实例,一个在您的测试中模拟,另一个在您的CarTankService构造函数中初始化。因此,您在验证模拟的情况时调用后一种情况

如果将CarClient转换为接口并将其注入,则解决方案如下所示:

[TestFixture]
class CarTankServiceTests
{
    private Mock<ITankQuery> TankQuery { get; set; }
    private ICarTankService CarTankService { get; set; }
    private Mock<CarClient> CarClient { get; set; }

    [SetUp]
    public void SetUp()
    {
        TankQuery = new Mock<ITankQuery>();
        CarClient = new Mock<CarClient>();
        CarTankService = new CarTankService(TankQuery.Object);
    }

    [Test]
    public void Add_NotExistReferenceNumber_AddTankReached()
    {
        TankQuery.Setup(uow => uow.isExist(It.IsAny<int>())).Returns(false);

        CarTankService.Add(new CarTank());

        CarClient.Setup(a=>a.AddTank(/*write you loginc*/));
        CarClient.Verify(uow => uow.AddTank(It.IsAny<ClientTank>()),Times.Once);
    }
}
下面是更多的解释:


编写CarTankService=new CarTankServiceTankQuery.Object时;在测试中,它会在类上创建一个新实例_carClient=new carClient;,因此,类有自己的实例,而测试类也有自己的实例CarClient=newmock;这是嘲笑。这行代码是CarTankService.addnewcartank;将坦克添加到类的实例中,而在测试中,您正在验证没有坦克CarClient.Verifyuow=>uow.AddTankIt.IsAny,Times.Once;的测试类实例

您需要一种方法将您的CarClient模拟传递给服务,因为它只是创建了一个私有实例,或者可以查看CarClient的私有实例。要做哪一个取决于您的体系结构和某种程度上的首选项—您应该更改多少实际代码才能对其进行测试。@stuartd这是我的想法,但我不想仅仅因为需要将此对象传递给我的服务就为我的模型类CarClient创建接口。还有其他方法吗?还是必须这样做?我认为通过接口传递模型对象看起来是不正确的。是的,到达AddTank意味着它必须正常工作,相应地Add必须正常工作。您应该以检查所有可能路径的方式编写测试。我的意思是,你必须在测试中设置一个环境,在这个环境中,你的函数的所有代码路径以及所有可能的输入都会被检查。因此,如果您的Add通过了所有测试,您就可以确定AddTank已经到达并且工作正常。然后,集中精力添加测试用例并检查所有可能的路径和输入。我认为达到能力不是单元测试的目标!如果可能的话,你必须检查每行代码的到达能力。那是不可能的。你有一个叫做加法的单位。如果您像TDD一样思考,我认为最好更改加法输出,以确保其正常工作并使其可测试,例如返回添加的油箱数。您是否同意,如果Add正常工作,则意味着到达ADDCANK并正常工作?是的,这是一个选项。您可以在AddTank中返回“True”或“False”,然后在Add中返回_carClient.AddTank。现在,您可以很容易地编写添加测试,并使用断言检查可访问性和功能性。我绝对不是测试专家,更不愿意就此发表任何意见。最好研究一下。关于Nunit.Verify,我认为它是为了确保函数或方法被调用多少次而设计的。想想递归函数,对于给定的输入应该被调用固定的次数,所以我认为它的应用程序不同于reach能力,但是我们可以用它来检查reach能力。