模拟暴露为 属性 的数组

Mocking an array exposed as a property

我得到了一些具有这种一般结构的代码:

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

我知道,我知道公开一个数组,虽然不是很好,但这是我一直在使用的东西。

无论如何,我正在寻找模拟 class B 并将一些数组元素设置为特定值以供使用代码使用。

似乎最小起订量无法直接执行此操作,但我看到了通过 class B 模拟模拟 class A 和 return 数组的建议.有点像这样:

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

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

我这么说是因为这实际上不起作用,我知道我应该尝试 return 类似 mockAArray.Object 的东西,但我找不到正确的语法来让它工作.

编辑: 使用 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 数组,那不是很好。这就是为什么我更喜欢嘲笑。显然,对于最简单的情况,只使用真实实例就可以了,不幸的是,现实世界并不总是允许我们采取简单的选择。

无论如何,最初的简单示例是为了简洁。

这取决于您在 Class Under Test (CUT) 中使用 class B 的方式。如果你有一些可能性如何将 class 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
}

然后 class 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);