Анонимные пространства имен делают код непроверенным

13

Вот типичный код C ++:

foo.hpp

#pragma once

class Foo {
public:
  void f();
  void g();
  ...
};

foo.cpp

#include "foo.hpp"

namespace {
    const int kUpperX = 111;
    const int kAlternativeX = 222;

    bool match(int x) {
      return x < kUpperX || x == kAlternativeX;
    }
} // namespace

void Foo::f() {
  ...
  if (match(x)) return;
  ...

Он выглядит как приличный идиоматический код C ++ - класс, вспомогательная функция, matchкоторая используется методами Foo, некоторые константы для этой вспомогательной функции.

И тогда я хочу написать тесты.
Было бы совершенно логично написать отдельный модульный тест для match, потому что это довольно нетривиально.
Но он находится в анонимном пространстве имен.
Конечно, я могу написать тест, который бы позвонил Foo::f(). Однако это не будет хорошим тестом, если Fooон тяжелый и сложный, такой тест не изолирует тестируемого от других не связанных факторов.

Так что мне нужно переместить matchи все остальное из анонимного пространства имен.

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

Abyx
источник
3
@ BЈовић перечитайте код - анонимное пространство имён foo.cpp, а не заголовок! OP, кажется, прекрасно понимает, что не следует помещать анонимные пространства имен в заголовок.
Амон
Некоторая идея в модульном тестировании кода C ++ в безымянном пространстве имен ... но суть в том, что инкапсуляция хороша. В конце концов, это та же проблема, что и у вас с закрытыми функциями-членами: они не могут быть протестированы ненавязчиво, но вы не хотите отказываться от скрытия информации для модульного тестирования (например, stackoverflow.com/a/3676680/3235496 ).
Манлио
1
Ваш случай концептуально не сильно отличается от случая тестирования (или не тестирования) частных методов. Поэтому, когда вы будете искать «модульный тест» здесь, в Программистах, вы получите множество ответов, которые вы можете применить непосредственно к вашему делу.
Док Браун
@DocBrown Я не спрашиваю, как проверить функции в ближайшее время. Пространства имен. Я спрашиваю "зачем помещать код в anon. Пространства имен?" (см. текст в конце вопроса). Виноват Иксрек за то, что сменил название на что-то другое.
Abyx
1
@Abyx: в этих других ответах, которые я упомянул выше, вы найдете здесь большое мнение многих экспертов о том, что тестировать частные методы - это действительно плохая идея, и что злоупотребление friendключевым словом для этой цели не рекомендуется. Сочетайте это с вашим предположением что если ограничение для метода приводит к ситуации, когда вы больше не можете тестировать его напрямую, это означает, что частные методы бесполезны.
Док Браун

Ответы:

7

Если вы хотите выполнить модульное тестирование частных деталей реализации, вы делаете тот же вид уклонения для безымянных пространств имен, что и для частных (или защищенных) членов класса:

Перерыв и вечеринка.

В то время как для классов вы злоупотребляете friend, для безымянных пространств имен вы злоупотребляете #include-механизмом, который даже не заставляет вас менять код.
Теперь, когда ваш тест-код (или лучше всего что-то, чтобы разоблачить все) находится в том же TU, проблем нет.

Предупреждение: если вы тестируете детали реализации, ваш тест будет прерван, если они изменятся. Обязательно проверяйте только те детали реализации, которые все равно будут утекать, или примите, что ваш тест необычайно эфемерен.

Deduplicator
источник
6

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

Какой смысл помещать функции и константы в анонимное пространство имен, если это делает их непригодными в тестах?

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

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

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

BЈовић
источник
5

Было бы совершенно логично написать отдельный модульный тест на совпадение, потому что это довольно нетривиально.

Код, который вы показали, matchявляется довольно тривиальным 1-строчным без каких-либо хитрых краевых случаев, или это похоже на упрощенный пример? Во всяком случае, я предполагаю, что это упрощено ...

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

Этот вопрос - то, что хотело заставить меня прыгнуть сюда, так как Deduplicator уже показал совершенно хороший способ взломать и получить доступ через #includeобман. Но формулировка здесь звучит так, как будто тестирование каждой внутренней детали реализации всего является своего рода универсальной конечной целью, когда она далека от этого.

Цель даже модульного тестирования не всегда состоит в том, чтобы протестировать каждый маленький гранулированный внутренний микроблок функциональности. Тот же вопрос относится к статическим функциям файловой области видимости в C. Вы можете даже сделать вопрос труднее ответить, спрашивая , почему разработчики используют pimplsв C ++ , который потребует как friendship и #includeфокусы в белой коробке, торгуясь легко тестируемости детали реализации для улучшения времени компиляции, например

С прагматической точки зрения это может показаться грубым, но matchможет быть неправильно реализовано в некоторых крайних случаях, которые приводят к его сбою. Однако, если единственный внешний класс, Fooкоторый имеет доступ к нему, matchне может использовать его таким образом, чтобы он сталкивался с этими граничными случаями, то это довольно не имеет значения для правильности того, Fooчто matchэти граничные случаи никогда не встретятся, если не произойдут Fooизменения, и в этот момент испытания Fooне пройдут, и мы сразу узнаем.

Более навязчивый образ мыслей, жаждущий проверить каждую внутреннюю деталь реализации (возможно, критически важное программное обеспечение, например), может захотеть вмешаться и повеселиться, но многие люди не обязательно думают, что это лучшая идея, так как это создаст самые хрупкие испытания, которые только можно представить. YMMV. Но я просто хотел обратиться к формулировке этого вопроса, которая звучит так, как будто такая тестируемость на уровне мелких деталей должна быть конечной целью, когда даже самый строгий подход к юнит-тестированию может немного расслабиться. и избегайте рентгеновских снимков каждого класса.

Итак, почему люди определяют функции в анонимных пространствах имен в C ++ или как статические функции в области файлов с внутренней связью в C, скрытые от внешнего мира? И это в основном так: скрывать их от внешнего мира. Это имеет ряд последствий от сокращения времени компиляции до уменьшения сложности (то, что не доступно в другом месте, не может вызвать проблемы в другом месте) и так далее. Вероятно, проверяемость деталей частной / внутренней реализации не является первоочередной задачей для людей, когда они делают это, скажем, сокращая время сборки и скрывая ненужную сложность от внешнего мира.


источник
Я хотел бы поднять это десять раз. Как касательная, связанная с критически важным, высоконадежным программным обеспечением, вы гораздо больше озабочены правильностью деталей. Этот идиоматический стиль для C ++ не подходит для этого. Я бы не использовал такие возможности языка, как анонимные пространства имен или шаблоны, похожие на прокси. Я бы грубо управлял своей границей с помощью [поддерживаемых] заголовков пространства пользователя и [неподдерживаемых] заголовков пространства разработчика.
Дэвид