2

在下面的示例中,我想测试该TestMe.DoSomething()功能。

我想模拟ISomething此方法中使用的接口并使其返回不同的值(取决于特定的单元测试。)

在现实生活中,ISomething界面最终调用了昂贵的 3rd 方资源——我绝对不想只调用一个真正的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()is private,我无法将该方法的返回值设置为我想要的。

关于如何解决这个问题的任何建议?

4

3 回答 3

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 的模拟。

虽然我认为您可能会抗议这个解决方案的变化太大,但我认为它比创建一个没有与成员密封的类更好,而成员是虚拟的唯一原因是为了测试。

于 2013-05-17T18:22:26.483 回答
1

将 SomethingFactory() 更改为 protected virtual 允许您使用 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();
于 2013-05-17T18:15:42.253 回答
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();
        }
    }
    
  2. 第二种方法是改变

    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;
        }
    }
    

这种技术称为“提取和覆盖”

于 2013-05-17T19:13:34.027 回答