我想用RhinoMocks模拟一个非常庞大的存储库,主要是为了完全实现庞大且经常变化的界面,而不是使用VisualStudio的“实现接口”默认实现(这需要为接口更新所有模拟改变并导致很多垃圾代码。)
我目前使用Stubs,但我还没有找到如何覆盖模拟的默认方法,除了定义每个可能的输入值。当使用bool TryGet(key,out值)模式时以及当我需要默认行为时,如果找不到键(这里:return false / null,在其他情况下:抛出异常),这尤其糟糕。
有没有办法在RhinoMocks中实现方法转发?
public interface IMyRepository
{
// would normally be implemented by database access
bool TryGetNameById(int id, out string name);
// more...
}
// within some other class:
public void SetupMockRepository()
{
IDictionary<int, string> namesByIds = new Dictionary<int, string>()
//create and fill with mock values
var mockRep = new MockRepository()
var repStub = mockRep.Stub<IMyRepository>()
// something like this, forward inner calls,
// without defining expected input values
var repStub.Stub(r => r.TryGetNameById(int id, out string name)
.OutRef((id) => (namesByIds.TryGetValue(id, out name) ? name : null))
.Return((id) => namesByIds.ContainsKey(id)));
}
编辑:
我现在尝试了一个代表,看起来更好,但仍有问题:
private delegate bool TryGet<TKey, TValue>(TKey key, out TValue value);
public void SetupMockRepository()
{
// code from above omitted
string outName;
_stub.Stub(r=>r.TryGetNameById(0, out outName))
.IgnoreArguments()
.Do(new TryGet<int,string>(namesByIds.TryGetValue))
}
这是可以接受的,但是当我运行它时,我得到一个InvalidOperationException: “上一个方法'TryGetValue(123)'需要返回值或异常才能抛出”
答案 0 :(得分:0)
您可以随时制作自己的假物品。通过混合使用部分模拟和部分模拟,您应该获得最大的灵活性。
public interface IMyRepository
{
bool TryGetNameById(int id, out string name);
}
public class FakeRepository : IMyRepository
{
// public on purpose, this is for testing after all
public readonly Dictionary<int, string> nameByIds = new Dictionary<int, string>();
// important to make all methods/properties virtual that are part of the interface implementation
public virtual bool TryGetNameById(int id, out string name)
{
return this.nameByIds.TryGetValue(id, out name);
}
}
您的设置将成为这样:
public void Setup()
{
var mock = MockRepository.GeneratePartialMock<FakeRepository>();
mock.nameByIds.Add(1, "test");
}
如果您需要更复杂的行为,您仍然可以存根方法调用,因为所有方法都是virtual
。
答案 1 :(得分:0)
这是我的第一个部分解决方案:
模拟对象在委托设置之后需要一个.Replay(),因为在调用MockRep.ReplayAll()之前使用了这些方法(在测试本身之前)。
我目前仅在少数情况下使用期望和记录/重放逻辑(并且不完全理解它是什么样的),并且发现模拟对象在看似正确的设置上失败是令人不安的。我只想要一个带有一些重写方法/属性的默认实现,如果有人更改了接口的不相关部分,这不会导致构建失败。
另一个新问题是设置.Do()委托,如
mockObj.Stub(o => o.DoSth(x))
.IgnoreArguments()
.Do(new Action(x => this.DoSth(x)))
在完全不同的方法上导致期望错误,该方法具有默认存根实现且未被更改。