C# 如何模拟私有字段?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/584769/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I mock a private field?
提问by Fung
I'm really new to mocks and am trying to replace a private field with a mock object. Currently the instance of the private field is created in the constructor. My code looks like...
我对模拟真的很陌生,并且正在尝试用模拟对象替换私有字段。目前,私有字段的实例是在构造函数中创建的。我的代码看起来像...
public class Cache {
private ISnapshot _lastest_snapshot;
public ISnapshot LatestSnapshot {
get { return this._lastest_snapshot; }
private set { this._latest_snapshot = value; }
}
public Cache() {
this.LatestSnapshot = new Snapshot();
}
public void Freeze(IUpdates Updates) {
ISnapshot _next = this.LastestSnapshot.CreateNext();
_next.FreezeFrom(Updates);
this.LastestSnapshot = _next;
}
}
What I'm trying to do is create a unit test that asserts ISnapshot.FreezeFrom(IUpdates)is called from within Cache.Freeze(IUpdates). I'm guessing I should replace the private field _latest_snapshotwith a mock object (maybe wrong assumption?). How would I go about that while still retaining a parameterless constructor and not resorting to making LatestSnapshot's set public?
我想要做的是创建一个单元测试,断言ISnapshot.FreezeFrom(IUpdates)从Cache.Freeze(IUpdates). 我猜我应该用_latest_snapshot模拟对象替换私有字段(可能是错误的假设?)。在仍然保留无参数构造函数并且不诉诸使LatestSnapshot's set public 的同时,我将如何处理?
If I'm totally going about writing the test the wrong way then please do point out as well.
如果我完全打算以错误的方式编写测试,那么也请指出。
The actual implementation of ISnapshot.FreezeFromitself calls a heirarchy of other methods with a deep object graph so I'm not too keen on asserting the object graph.
其ISnapshot.FreezeFrom自身的实际实现调用具有深层对象图的其他方法的层次结构,因此我不太热衷于断言对象图。
Thanks in advance.
提前致谢。
采纳答案by Alex B
I'm almost citing techniques from "Working Effectively with Legacy Code":
我几乎引用了“有效地使用遗留代码”中的技术:
- Sub-class your class in a unit test and supersede your private variable with a mock object in it (by adding a public setter or in the constructor). You probably have to make the variable protected.
- Make a protected getter for this private variable, and override it in testing subclass to return a mock object instead of the actual private variable.
- Create a protected factory method for creating
ISnapshotobject, and override it in testing subclass to return an instance of a mock object instead of the real one. This way the constructor will get the right value from the start. - Parametrize constructor to take an instance of
ISnapshot.
- 在单元测试中对类进行子类化,并用其中的模拟对象取代私有变量(通过添加公共 setter 或在构造函数中)。您可能必须使变量受保护。
- 为这个私有变量创建一个受保护的 getter,并在测试子类中覆盖它以返回一个模拟对象而不是实际的私有变量。
- 创建用于创建
ISnapshot对象的受保护工厂方法,并在测试子类中覆盖它以返回模拟对象的实例而不是真实的实例。这样构造函数将从一开始就获得正确的值。 - 参数化构造函数以获取
ISnapshot.
回答by Jason
I don't think you'd need to mock private member variables. Isn't the whole idea of mocking that the public interface for an object works as expected? Private variables are implementation details that mocks aren't concerned with.
我认为您不需要模拟私有成员变量。嘲笑对象的公共接口是否按预期工作的整个想法不好吗?私有变量是模拟不关心的实现细节。
回答by mezoid
I'm not sure that you can do that. If you're wanting to test _nextthen you're probably going to have to pass it in as a parameter and then in your unit test pass in a Mock object which you can then test using an Expectation. That's what I'd be doing if I were trying to do it in Moq.
我不确定你能不能做到。如果您想进行测试,_next那么您可能必须将其作为参数传入,然后在单元测试中传入 Mock 对象,然后您可以使用 Expectation 进行测试。如果我试图在 Moq 中做到这一点,这就是我要做的。
As an example of what I might try using the Moq framework:
作为我可能尝试使用 Moq 框架的示例:
Mock<ISnapshot> snapshotMock = new Mock<ISnapshot>();
snapshotMock.Expect(p => p.FreezeFrom(expectedUpdate)).AtMostOnce();
Cache c = new Cache(snapshotMock.Object);
c.Freeze(expectedUpdate);
Note: I haven't tried to compile the above code. Its just there to give an example of how I'd approach solving this.
注意:我没有尝试编译上面的代码。它只是为了举例说明我将如何解决这个问题。
回答by matt b
This answer might be simple, but looking at the code, is there any way in which ISnapshot.FreezeFrom(IUpdates)is not going to be called? Sounds like you want to assert something that will always be true.
这个答案可能很简单,但是看看代码,有没有什么方法ISnapshot.FreezeFrom(IUpdates)不会被调用?听起来你想要断言一些永远正确的东西。
As Jason says, mocking is meant for situations where your class depends on SomeInterfaceto do it's work, and you want to test YourClassin isolation from whichever implementation of SomeInterfaceyou actually use at runtime.
正如 Jason 所说,模拟适用于您的类依赖于SomeInterface完成它的工作的情况,并且您希望YourClass独立于SomeInterface您在运行时实际使用的任何实现进行测试。
回答by Steve Freeman
The question to ask is: what are the externally visible effects if this worked?
要问的问题是:如果这样做有效,外部可见的影响是什么?
What happens to all those Snapshots? One option might to initialise the Cache with its first Snapshot from outside, say in the constructor. Another might be to mock whatever it is that the Snapshot calls that matters outside the cache. It depends on what you care that happens.
所有这些快照会发生什么?一种选择可能是使用外部的第一个快照初始化缓存,例如在构造函数中。另一个可能是模拟 Snapshot 调用在缓存之外很重要的任何内容。这取决于你关心发生的事情。
回答by user231038
It might be too late to respond. Anyways. I also had similar problem.
现在反应可能为时已晚。无论如何。我也有类似的问题。
public class Model
{
public ISomeClass XYZ{
get;
private set;
}
}
I required to set value of XYZ in my test case. I resolved the problem using this syntex.
我需要在我的测试用例中设置 XYZ 的值。我使用这个语法解决了这个问题。
Expect.Call(_model.XYZ).Return(new SomeClass());
_repository.ReplayAll();
In the case above we can do it like this
在上面的情况下,我们可以这样做
Expect.Call(_cache.LatestSnapshot).Return(new Snapshot());
_repository.ReplayAll();
回答by Dawit
Turn Cache into a template as shown below.
将 Cache 变成一个模板,如下图所示。
template <typename T=ISnapshot>
public class Cache {
private T _lastest_snapshot;
public T LatestSnapshot {
get { return this._lastest_snapshot; }
private set { this._latest_snapshot = value; }
}
public Cache() {
this.LatestSnapshot = new Snapshot();
}
public void Freeze(IUpdates Updates) {
T _next = this.LastestSnapshot.CreateNext();
_next.FreezeFrom(Updates);
this.LastestSnapshot = _next;
}
}
In production code do:
在生产代码中做:
Cache<> foo;//OR
Cache<ISnapshot> bar;
In test code do:
在测试代码中做:
Cache<MockSnapshot> mockFoo;
回答by murki
You will probably have to refactor your class like this, in order for it to be injected with a different dependency for ISnapshot. Your class will remain to function the same.
您可能必须像这样重构您的类,以便为 ISnapshot 注入不同的依赖项。您的类将保持相同的功能。
public class Cache {
private ISnapshot _lastest_snapshot;
public ISnapshot LatestSnapshot {
get { return this._lastest_snapshot; }
private set { this._latest_snapshot = value; }
}
public Cache() : this (new Snapshot()) {
}
public Cache(ISnapshot latestSnapshot) {
this.LatestSnapshot = latestSnapshot;
}
public void Freeze(IUpdates Updates) {
ISnapshot _next = this.LastestSnapshot.CreateNext();
_next.FreezeFrom(Updates);
this.LastestSnapshot = _next;
}
}
回答by Vanuan
You can simply add "setSnapshot(ISnapshot)" method to the Cache with your mocked class instance.
您可以使用模拟的类实例简单地将“setSnapshot(ISnapshot)”方法添加到缓存中。
You can also add a constructor that takes ISnapshot.
您还可以添加一个采用 ISnapshot 的构造函数。

