模拟暴露为 属性 的数组
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
{ ... }
然后在测试中模拟 IPortManager
和 ISerialPort
并在 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);
我得到了一些具有这种一般结构的代码:
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
{ ... }
然后在测试中模拟 IPortManager
和 ISerialPort
并在 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);