如何创建单元测试来测试位于循环中的类?
这是情景。
我有一个注入了串行端口引用的类。
该类有一个名为Send(String data);
的方法这个公共方法调用一个同步的私有方法来完成实际的工作。
当调用此方法时,被测试类(CUT)应该执行以下操作。
1)将字符串分成字符。
2)发送一个字母
3)等待char回应
4)发送下一个字符(重复直到所有字符发送)
因此,在发送第一个字符后,CUT将处于循环中等待回显,直到它收到一个或超时。
我遇到的问题是,一旦CUT进入此循环,它将阻止测试类,直到它超时。
因为我需要测试类将回声发送到CUT即时卡住。
为了测试这个,我创建了一个模拟串口,并使用NUnit。
测试如下。我的想法是在发送测试字符串后,我等待CUT响应。每次CUT将字符写入串口时,等待都会被取消,我会向串口写回信,而CUT会通过发送下一个字符来响应。
[Test]
public void Test()
{
_serialPort.DataSentEvent += new EventHandler(_serialPort_DataSentEvent);
_completedSync = new ManualResetEvent(false);
_wrapperPort.Send("TEST");
_completedSync.WaitOne(1000); // wait for first char
Assert.AreEqual("T", _serialPort.BufferOUT); //first char sent
_serialPort.BufferIN = "T"; //write the echo
_completedSync.WaitOne(1000); //wait for second char
Assert.AreEqual("E", _serialPort.BufferOUT); //second char sent
//...etc
}
void _serialPort_DataSentEvent(object sender, EventArgs e)
{
_completedSync.Set();
}
但是,一旦调用Send(“TEST”),控制只返回测试类,一旦CUT超时等待回声,就会发生CUT阻塞。
由于Send方法正在另一个线程上完成,为什么它会阻塞测试类?
答案 0 :(得分:1)
我认为你在实验中混合了太多的实施细节。
您已经非常精确地指定了串行端口协议,因此我将定义一个描述该协议的接口:
public interface ISerialPort
{
event EventHandler Echo;
void Send(char c);
}
此界面强制我一次发送一个字符。正确使用此接口时,串口包装器将正常工作。 这种方法需要额外的实际串口包装类。这个类需要处理异步操作才能正确实现接口。
使用TestSerialPort
,我可以按如下方式编写测试:
[TestMethod]
public void Send_data_as_chars_to_serial_port()
{
const string data = "TEST";
var serialPort = new TestSerialPort();
var wrapperPort = new WrapperPort(serialPort);
wrapperPort.Send(data);
Assert.AreEqual(data, serialPort.Buffer);
Assert.AreEqual(data.Length, serialPort.SendCount);
}
TestSerialPort
实现接口,模拟真实的串口行为,并公开测试可以断言的数据:
public class TestSerialPort : ISerialPort
{
public string Buffer;
public int SendCount;
public void Send(char c)
{
SendCount++;
Buffer += c;
Echo.Invoke(this, EventArgs.Empty);
}
public event EventHandler Echo;
}
满足上述测试的一种可能的实现方式:
public class WrapperPort
{
private readonly ISerialPort serialPort;
private Queue<char> buffer;
private char current;
public WrapperPort(ISerialPort serialPort)
{
this.serialPort = serialPort;
serialPort.Echo += OnEcho;
}
public void Send(string data)
{
buffer = new Queue<char>(data);
SendNextCharacter();
}
private void OnEcho(object sender, EventArgs e)
{
SendNextCharacter();
}
private void SendNextCharacter()
{
if (buffer.Any() == false) return;
current = buffer.Dequeue();
serialPort.Send(current);
}
}