模拟作为属性公开的数组

时间:2015-07-29 11:16:18

标签: c# arrays unit-testing mocking moq

我已经获得了一些具有这种通用结构的代码:

public class A
{
    int Foo { get; set; }
    byte Bar { get; set; }
}

public class B
{
    A[] _Baz = new A[10];

    A[] Baz
    {
        get { return _Baz; }
        set { _Baz = value; }
    }
}

我知道,我知道暴露一个阵列,不是很好,但它是我给予的工作。

无论如何,我希望模拟B类并将一些数组元素设置为消费代码使用的特定值。

似乎moq不能直接执行此操作,但我已经看到了模拟A类数组并通过B类模拟返回的建议。有点像:

var mockAArray = new Mock<A>[10];
mockAArray[3] = new Mock<B>();

var mockB = new Mock<B>();
mockB.Setup(x => x.A).Returns(mockAArray);

我说的有点因为实际上没有用,我知道我应该尝试返回类似mockAArray.Object的内容,但我无法找到正确的语法来实现这一点。< / p>

编辑: 使用dee提供的答案,上面例子的正确语法是:

IA[] tmpAArray = new A[10];
var mockA = new Mock<IA>;
mockA.Setup(x => x.Foo).Returns(1234);
tmpAArray[3] = mockA.Object;

var mockB = new Mock<IB>;
mockB.Setup(y => y.Baz).Returns(tmpAArray);

结束编辑

最终,我想重构代码以摆脱暴露的数组,但我也希望能够通过这个测试来帮助捕获任何破坏

编辑: 好的,我被问到一个更复杂的情况,我想考虑这样做。所以这里的另一个稍微复杂一点,请记住不是真的,例如。

public class MySerialPort : ISerialPort
{
    SerialPort _port;
    ... // properties exposing _port properties

    MySerialPort()
    {
        _port = new SerialPort;
    }

    void Open()
    {
        _port.Open();
    }

    void Close()
    {
        _port.Close();
    }
    ... // more methods 
}

public class PortManager
{
    ISerialPort[] ports = new MySerialPort[10];

    ISerialPort[] Port
    {
        get { return ports; }
        set { ports = value; }
    }

    ... // properties for managing the ports

    ... // some methods for good measure
}

public class TestClass()
{
    int SomeMagicRoutine(PortManager manager)
    {
        // Some routine that takes the values in manager
        // and the values in ports and returns an answer.
    }
}

只是启动一个真实的MySerialPort和管理器实例数组会使测试变得困难,想象一下如果是一个Streams数组而不是很好。这就是为什么我更喜欢嘲笑。显然,对于最简单的情况,只使用真实实例就可以了,不幸的是,现实世界并不总是允许我们采取简单的选择。

无论如何,最初的简单例子是试图简洁。

1 个答案:

答案 0 :(得分:1)

这取决于您在B中使用课程Class Under Test (CUT)的方式。如果您有可能如何将类B的实例注入CUT,那么您不需要模拟。

例如,使用构造函数注入,你会有类似的东西。

public class CUT
{
    private B _b;

    public CUT(B b)
    {
        _b = b;
    }
}

[TestMethod]
public void test01()
{
    // Arrange
    // here you create your fake-array
    A[] bazFake = new A[] { new A { Foo = 1, Bar = 2 } };

    B bFake = new B();
    bFake.Baz = bazFake;

    // and inject this fake-object into the CUT
    CUT cut = new CUT(bFake);

    // Act
    // ...

    // Assert
    // ...
}

编辑:

然后,使用Moq的最简单方法是创建IPortManager界面:

public interface IPortManager
{
    ISerialPort[] Port { get; set; }

    // properties for managing the ports

    // some methods for good measure
}

然后班级PortManager将实施它:

public class PortManager : IPortManager
{ ... }

然后在测试中模拟IPortManagerISerialPort并在CUT中使用它:

ISerialPort[] fakePorts = new MySerialPort[10];

// create your fake ports here and setup methods according to your needs
int openedPorts = 0; // just an example, I don't know what you are going to test
Mock<ISerialPort> port1Stub = new Mock<ISerialPort>();
port1Stub.Setup(p => p.Open()).Callback(() => openedPorts++);
fakePorts[0] = port1Stub.Object; 

Mock<IPortManager> managerStub = new Mock<IPortManager>();
managerStub.Setup(m => m.Port).Returns(fakePorts);

TestClass cut = new TestClass();
cut.SomeMagicRoutine(managerStub.Object);