Rhino Mocks 异常 "Expected #1, Actual #0" 在明显有效的代码中
Rhino Mocks exception "Expected #1, Actual #0" in apparently working code
我有一段来自一本关于 MVVM 的旧书的代码可以运行,但是使用 Rhino Mocks 的测试失败并显示以下消息:
Test method TestProject.UnitTest1.UpdateCustomer_Always_CallsUpdateWithCustomer threw exception:
Rhino.Mocks.Exceptions.ExpectationViolationException: DataProvider.DoSomething(ConsoleApp.Customer); Expected #1, Actual #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));
}
}
}
我阅读了几篇关于这个结果的帖子,例如1 and 2, but this doesn't help me. I read this answer 这看起来很有趣:
I usually get this error when a stubbed method is called with an object argument
that I build in the test and in the tested code the object is built before calling that method.
虽然这可能是 Rhino Mocks 失败的原因,但它似乎是一个错误。
我的问题是:是不是我的测试有问题,Rhino Mocks有bug,还是我的代码有问题?
测试存根 DataProvider.GetCustomer
到 return 一个预期的客户实例将在断言中使用,但示例视图模型不调用 DataProvider.GetCustomer
。
它正在使用构造函数中初始化的那个。
//...
public ViewModel (DataProvider dataProvider, string id) {
_dataProvider = dataProvider;
_customer = new Customer { ID = id };
}
//...
因此,根据所示示例,测试抛出的异常是准确的。
这是因为视图模型使用的实际实例将不是测试时断言中使用的实例。
//...
dataProviderMock.AssertWasCalled (d => d.DoSomething (expectedCustomer));
为了使测试根据其安排按预期运行,实际上将重构视图模型以与初始化 Customer
分离
例如
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]
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));
}
}
注意断言中使用的委托来验证预期参数的特征,而不是调用它时传递的特定实例。
我有一段来自一本关于 MVVM 的旧书的代码可以运行,但是使用 Rhino Mocks 的测试失败并显示以下消息:
Test method TestProject.UnitTest1.UpdateCustomer_Always_CallsUpdateWithCustomer threw exception: Rhino.Mocks.Exceptions.ExpectationViolationException: DataProvider.DoSomething(ConsoleApp.Customer); Expected #1, Actual #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));
}
}
}
我阅读了几篇关于这个结果的帖子,例如1 and 2, but this doesn't help me. I read this answer 这看起来很有趣:
I usually get this error when a stubbed method is called with an object argument that I build in the test and in the tested code the object is built before calling that method.
虽然这可能是 Rhino Mocks 失败的原因,但它似乎是一个错误。
我的问题是:是不是我的测试有问题,Rhino Mocks有bug,还是我的代码有问题?
测试存根 DataProvider.GetCustomer
到 return 一个预期的客户实例将在断言中使用,但示例视图模型不调用 DataProvider.GetCustomer
。
它正在使用构造函数中初始化的那个。
//...
public ViewModel (DataProvider dataProvider, string id) {
_dataProvider = dataProvider;
_customer = new Customer { ID = id };
}
//...
因此,根据所示示例,测试抛出的异常是准确的。
这是因为视图模型使用的实际实例将不是测试时断言中使用的实例。
//...
dataProviderMock.AssertWasCalled (d => d.DoSomething (expectedCustomer));
为了使测试根据其安排按预期运行,实际上将重构视图模型以与初始化 Customer
例如
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]
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));
}
}
注意断言中使用的委托来验证预期参数的特征,而不是调用它时传递的特定实例。