Асинхронный TDD - блоки класса теста - PullRequest
2 голосов
/ 07 октября 2010

Как создать модульный тест для проверки класса, который находится в цикле?

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

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

В классе есть метод Send (String data);

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

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

1) разбить строку на символы.
2) отправить символ
3) дождаться отображения символа
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();
    }

Но что происходит,CUT блокируется, как только вызывается Send ("TEST"), и управление возвращается к тестовому классу только после истечения времени ожидания CUT, ожидающего эхо.

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

1 Ответ

1 голос
/ 30 декабря 2010

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

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

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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...