Дублирующий код между модульным тестом и реализацией

В настоящее время я разрабатываю некоторые низкоуровневые драйверы для встроенной платформы на обычном языке C. Я использую Unity+ Cmock в качестве основы для модульного тестирования

Однако при написании низкоуровневых вещей я часто сталкиваюсь со следующей схемой:

Тестовое задание:

void test_mcp2515_read_register(void)
{
    spi_frame_t expected_frame = {{0}};
    expected_frame.tx_length = 2;
    expected_frame.rx_length = 3;
    expected_frame.tx_data[0] = MCP2515_READ_CMD;
    expected_frame.tx_data[1] = TEST_ADDR;
    expected_frame.callback = callback_test;

    spi_transmit_ExpectAndReturn(expected_frame, true);

    mcp2515_read_register(TEST_ADDR, callback_test);
}

Реализация:

void mcp2515_read_register(uint8_t addr, spi_callback callback)
{
    spi_frame_t frame = {{0}};
    frame.tx_length = 2;
    frame.rx_length = 3;
    frame.tx_data[0] = MCP2515_READ_CMD;
    frame.tx_data[1] = addr;
    frame.callback = callback;

    spi_transmit(frame);
}

Как видите, между тестом и реализацией кода много дублирования.

Это проблема? Я неправильно пишу свои тесты? Или мне вообще не стоит писать тесты для такого низкого уровня?

1 ответ

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

В вашем случае, возможно, вы могли бы разделить функцию mcp2515_read_register на две части: одну, которая создает структуру, и другую, которая обрабатывает передачу SPI.

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

  • SPI драйвер касается только реального общения.
  • Драйвер контроллера CAN касается только специфики контроллера.
  • Вызывающий абонент ("главный" или любой другой).
  • Тестовый код для драйвера контроллера CAN: заменяет основной.

Драйвер SPI объявляет spi_frame_t как непрозрачный тип, структура, которая касается только данных и связи SPI. Никто за пределами драйвера SPI не знает и не должен знать содержимое этой структуры. Я не знаю, что делает функция обратного вызова, но это не похоже на то, что связано с драйвером SPI. Это скорее похоже на то, что связано с кодом, который вызывает драйвер SPI.

Драйвер контроллера CAN включает в себя драйвер SPI. Он вызывает "конструктор" из драйвера SPI для создания кадра, а затем передает кадр процедурам связи SPI. Драйвер контроллера CAN не имеет тесной связи с функциональностью SPI. Например, не имеет смысла переписывать код вашего контроллера CAN, потому что вы нашли ошибку в SPI. Также вам не нужен контроллер CAN, чтобы иметь возможность использовать SPI, если вы хотите повторно использовать драйвер SPI в другом проекте.

Тестовый случай заменяет вызывающего ("основного") или код контроллера CAN, в зависимости от того, какую часть программы вы пытаетесь протестировать. Он использует те же функции, что и производственный код.

Другие вопросы по тегам