Moq在被测试的方法内部创建的对象
本文关键字:内部 创建 对象 方法 Moq 测试 | 更新日期: 2023-09-27 18:14:03
在下面的示例中,我想测试TestMe.DoSomething()
函数。
我想模拟此方法中使用的ISomething
接口,并使其返回不同的值(取决于特定的单元测试)
在现实生活中,ISomething
接口最终调用昂贵的第三方资源——我绝对不想只调用真正的ISomething
。
结构示例如下:
class TestMe
{
public void DoSomething()
{
ISomething s = SomethingFactory();
int i = s.Run();
//do things with i that I want to test
}
private ISomething SomethingFactory()
{
return new Something();
}
}
interface ISomething
{
int Run();
}
class Something : ISomething
{
public int Run()
{
return 1;
}
}
下面是不能工作的代码:
var fakeSomething = new Mock<ISomething>();
var testMe = new TestMe();
Mock.Get(testMe).Setup(p => p.SomethingFactory()).Returns(fakeSomething.Object);
testMe.DoSomething();
因为SomethingFactory()
是private
,所以我不能将该方法的返回值设置为我想要的值。
关于如何解决这个问题有什么建议吗?
使工厂成为一个完整的接口/类,并从TestMe中删除SomethingFactory方法。
public interface ISomethingFactory {
ISomething MakeSomething();
}
public sealed class SomethingFactory {
public ISomething MakeSomething() {
return new Something();
}
}
class TestMe
{
private readonly ISomethingFactory _somethingFactory;
public TestMe(ISomethingFactory somethingFactory) {
_somethingFactory = somethingFactory;
}
public void DoSomething()
{
ISomething s = _somethingFactory.MakeSomething();
int i = s.Run();
//do things with i that I want to test
}
}
这将允许你模拟issomethingfactory来返回一个模拟的issomething。
虽然我认为你可能会抗议这个解决方案过于激烈的变化,我认为这比创建一个不与成员密封的类要好,这些成员作为虚拟的唯一原因是为了测试。
-
你可以注入你的依赖。如果不想破坏所有调用者,可以添加两个构造函数,并使用允许在tests
中注入fake的构造函数。class TestMe { private readonly ISomething something; TestMe() : this(new RealSomething() { } TestMe(ISomething sth) { something = sth; } public void DoSomething() { ISomething s = SomethingFactory(); int i = s.Run(); //do things with i that I want to test } private ISomething SomethingFactory() { return new Something(); } }
-
第二种方法是改变
SomethingFactory
方法来保护虚拟,并在派生类中重写它并使用该类,或者设置
class TestableTestMe : TestMe { private readonly ISomething something; TestableTestMe(ISomething testSpecific) { something = testSpecific; } public void DoSomething() { ISomething s = SomethingFactory(); int i = s.Run(); //do things with i that I want to test } protected override ISomething SomethingFactory() { return something; } }
这种技术叫做"提取和覆盖"
将SomethingFactory()更改为受保护的虚拟允许您使用Moq。
public class TestMe
{
public void DoSomething()
{
ISomething s = SomethingFactory();
int i = s.Run();
//do things with i that I want to test
}
protected virtual ISomething SomethingFactory()
{
return new Something();
}
}
public interface ISomething
{
int Run();
}
public class Something : ISomething
{
public int Run()
{
return 1;
}
}
所以你可以运行这个测试:
var fakeSomething = new Mock<ISomething>();
fakeSomething.Setup(p => p.Run()).Returns(2);
var testMe = new Mock<TestMe>();
testMe.Protected().Setup<ISomething>("SomethingFactory").Returns(fakeSomething.Object);
testMe.Object.DoSomething();