C# 犀牛嘲弄例外”;预期为1,实际为0;在工作代码中

C# 犀牛嘲弄例外”;预期为1,实际为0;在工作代码中,c#,rhino-mocks,C#,Rhino Mocks,我有一段关于MVVM的旧书中的代码,它是有效的,但使用Rhino Mocks的测试失败,并显示以下消息: 测试方法TestProject.UnitTest1.UpdateCustomer\u Always\u CallsUpdateWithCustomer引发异常: Rhino.Mocks.Exceptions.ExpectationViolationException:DataProvider.DoSomething(ConsoleApp.Customer);预期为1,实际为0 我提供了一个我

我有一段关于MVVM的旧书中的代码,它是有效的,但使用Rhino Mocks的测试失败,并显示以下消息:

测试方法TestProject.UnitTest1.UpdateCustomer\u Always\u CallsUpdateWithCustomer引发异常: Rhino.Mocks.Exceptions.ExpectationViolationException:DataProvider.DoSomething(ConsoleApp.Customer);预期为1,实际为0

我提供了一个我认为相当的例子:

namespace ConsoleApp {
    class Program { static void Main () { } }

    public class Customer { public string ID { get; set; } }

    public class DataProvider {
        public virtual Customer GetCustomer (string id) => new Customer ();
        public virtual void DoSomething (Customer customer) { }
    }

    public class ViewModel {
        DataProvider _dataProvider;
        Customer _customer;

        public ViewModel (DataProvider dataProvider, string id) {
            _dataProvider = dataProvider;
            _customer = new Customer { ID = id };
        }

        public void DoSomething () => _dataProvider.DoSomething (_customer);
    }
}
以及失败的测试

namespace TestProject {
    [TestClass]
    public class UnitTest1 {
        [TestMethod]
        public void UpdateCustomer_Always_CallsUpdateWithCustomer () {
            DataProvider dataProviderMock = MockRepository.GenerateMock<DataProvider> ();
            Customer expectedCustomer = new Customer ();
            dataProviderMock.Stub (u => u.GetCustomer (Arg<string>.Is.Anything)).Return (expectedCustomer);
            ViewModel target = new ViewModel (dataProviderMock, string.Empty);
            target.DoSomething ();
            dataProviderMock.AssertWasCalled (d => d.DoSomething (expectedCustomer));
        }
    }
}
名称空间测试项目{
[测试类]
公共类UnitTest1{
[测试方法]
public void UpdateCustomer\u Always\u CallsUpdateWithCustomer(){
DataProvider dataProviderMock=MockRepository.GenerateMock();
客户期望客户=新客户();
dataProviderMock.Stub(u=>u.GetCustomer(Arg.Is.Anything)).Return(expectedCustomer);
ViewModel目标=新的ViewModel(DataProviderLock,string.Empty);
target.DoSomething();
调用DataProviderLock.Assertwas(d=>d.DoSomething(expectedCustomer));
}
}
}
我读了几篇关于这个结果的帖子,例如和,但这对我没有帮助。我读了一本似乎很有趣的书:

当使用对象参数调用存根方法时,通常会出现此错误 我在测试和测试代码中构建的对象是在调用该方法之前构建的

虽然这可能是犀牛模仿失败的原因,但它似乎是一个bug


我的问题是:我的测试中是否有错误,Rhino Mocks中是否有错误,或者我的代码是否有问题?

测试存根
DataProvider.GetCustomer
,以返回要在断言中使用的预期客户实例,但示例视图模型不调用
DataProvider.GetCustomer

它正在使用构造函数中初始化的

//...

public ViewModel (DataProvider dataProvider, string id) {
    _dataProvider = dataProvider;
    _customer = new Customer { ID = id };
}

//...
因此,在给出的示例中,测试引发的异常是准确的

这是因为在执行测试时,视图模型使用的实际实例将不是断言中使用的实例

//...

dataProviderMock.AssertWasCalled (d => d.DoSomething (expectedCustomer));
为了使测试根据其安排按预期运行,视图模型实际上必须进行重构,以与初始化
客户

比如说

public class ViewModel {
    DataProvider _dataProvider;
    string id;

    public ViewModel (DataProvider dataProvider, string id) {
        _dataProvider = dataProvider;
        this.id = id;
    }

    public void DoSomething () {
        Customer customer = _dataProvider.GetCustomer(id);
        _dataProvider.DoSomething (_customer);
    }
}
测试还应该更明确地说明它要测试什么

[TestClass]
public class UnitTest1 {
    [TestMethod]
    public void UpdateCustomer_Always_CallsUpdateWithCustomer () {
        //Arrange
        DataProvider dataProviderMock = MockRepository.GenerateMock<DataProvider> ();
        string id = "FakeId";
        Customer expectedCustomer = new Customer { ID = id };
        dataProviderMock.Stub (u => u.GetCustomer (id))
            .Return (expectedCustomer);
        ViewModel target = new ViewModel (dataProviderMock, id);

        //Act
        target.DoSomething ();

        //Assert
        dataProviderMock.AssertWasCalled (d => d.DoSomething (expectedCustomer));
    }
}
[TestClass]
公共类UnitTest1{
[测试方法]
public void UpdateCustomer\u Always\u CallsUpdateWithCustomer(){
//安排
DataProvider dataProviderMock=MockRepository.GenerateMock();
字符串id=“FakeId”;
客户期望客户=新客户{ID=ID};
dataProviderLock.Stub(u=>u.GetCustomer(id))
.退货(预期客户);
ViewModel目标=新的ViewModel(dataProviderMock,id);
//表演
target.DoSomething();
//断言
调用DataProviderLock.Assertwas(d=>d.DoSomething(expectedCustomer));
}
}
或者,如果视图模型符合预期,那么测试需要以不同的方式声明其期望

[TestClass]
public class UnitTest1 {
    [TestMethod]
    public void UpdateCustomer_Always_CallsUpdateWithCustomer () {
        //Arrange
        DataProvider dataProviderMock = MockRepository.GenerateMock<DataProvider> ();
        string id = "FakeId";            
        ViewModel target = new ViewModel (dataProviderMock, id);

        //Act
        target.DoSomething ();

        //Assert
        dataProviderMock
            .AssertWasCalled (d => d.DoSomething (Arg<Customer>.Matches(c => c.ID == id));
    }
}
[TestClass]
公共类UnitTest1{
[测试方法]
public void UpdateCustomer\u Always\u CallsUpdateWithCustomer(){
//安排
DataProvider dataProviderMock=MockRepository.GenerateMock();
字符串id=“FakeId”;
ViewModel目标=新的ViewModel(dataProviderMock,id);
//表演
target.DoSomething();
//断言
dataProviderMock
.AssertWasCalled(d=>d.DoSomething(Arg.Matches(c=>c.ID==ID));
}
}

请注意,断言中使用的委托用于验证预期参数的特征,而不是在调用该参数时传递的特定实例。

您可能需要提供……仅显示一些代码,很难看到整体情况。@AlexeiLevenkov:您说得对,完成。@mins您的示例视图模型没有调用
DataProvider.GetCustomer
。它使用的是构造函数中初始化的异常。因此,给定所示示例,测试引发的异常是准确的。“测试只是检查DataProvider.DoSomething是用某个对象调用的”据我所知,不是文章中的代码所显示的……而是“
DoSomething
正是使用
expectedCustomer
调用的。”“示例视图模型没有调用DataProvider.GetCustomer。它使用构造函数中初始化的一个”
DataProvider.GetCustomer>被模拟覆盖以返回它自己的对象:
dataProviderMock.Stub(u=>u.GetCustomer(Arg.Is.Anything)).Return(expectedCustomer)
。我可以更新我的示例,但这不会改变错误(我可能错了)在您的示例中,@mins
GetCustomer
从未被使用。您正在viewmodel构造函数中手动创建实例。您的答案是正确的,因为我的问题,所以我接受它。我问题中的错误代码不能代表产生错误的代码,因此答案无法重复使用。如果可以简化,我将发布一个新问题请用一个好的示例演示我的代码。@mins已理解。请确保您的示例准确地反映了实际问题。