在以下示例中,我想测试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
,我无法将该方法的返回值设置为我想要的。
关于如何解决这个问题的任何建议?
答案 0 :(得分:3)
使工厂成为完整的接口/类,并从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
}
}
这将允许您模拟ISomethingFactory以返回ISomething的模拟。
虽然我认为你可能会对这个解决方案提出太大的改变,但我认为这比制作一个没有成为虚拟的唯一理由的成员的课程要好进行测试更好。
答案 1 :(得分:1)
您可以注入依赖项。如果您不想打破所有调用者,可以添加两个构造函数并使用允许您在测试中注入假的构造函数
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;
}
}
这种技术称为“提取和覆盖”
答案 2 :(得分:0)
将SomethingFactory()更改为受保护虚拟允许您使用Moq.Protected通过其名称访问该方法:
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();