2010-10-07 2 views
2

Как создать единичный тест для тестирования класса, который находится в цикле?Асинхронные TDD - блоки тестового класса

Вот сценарий.

У меня есть класс, которому вводят ссылку на последовательный порт.

Класс имеет метод Send (String data);

Этот общедоступный метод вызывает асинхронный частный метод для выполнения фактической работы.

Испытание класса (CUT) должно выполняться следующим образом при вызове этого метода.

1) Разделите строку на символы.
2) отправить символ
3) ждать полукокса быть эхо
4) посылают следующий символ (повторить, пока все символы не послали)

Таким образом, после отправки первого полукокса вырезанные будут сидеть в цикле ожидая эха, пока он не получит один или не выйдет.

Проблема, которая возникает после того, как CUT вошел в этот цикл, он заблокирует тестовый класс, пока не истечет время.

Поскольку мне нужен тестовый класс для отправки эха на CUT im stuck.

Чтобы проверить это, я создал mock последовательный порт и im, используя 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 истекло время ожидания эхо.

Поскольку метод отправки завершается в другом потоке, почему он блокирует тестовый класс?

+0

было бы неплохо увидеть код 'CUT' – Gutzofter

ответ

1

Я думаю, что вы смешиваете слишком много деталей реализации в свой тест.

Вы указали серийный протокол порта очень точно, поэтому я хотел бы определить интерфейс, который описывает этот протокол:

public interface ISerialPort 
{ 
    event EventHandler Echo; 

    void Send(char c); 
} 

Этот интерфейс заставляет меня посылать по одному символу за раз. Оболочка последовательного порта будет работать правильно, если он правильно использует этот интерфейс. Этот подход требует дополнительного класса-оболочки для реального последовательного порта. Именно этот класс должен обрабатывать операции async, чтобы правильно реализовать интерфейс.

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