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


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и все остальное из анонимного пространства имен.

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


3
@ BЈовић перечитайте код - анонимное пространство имён foo.cpp, а не заголовок! OP, кажется, прекрасно понимает, что не следует помещать анонимные пространства имен в заголовок.
Амон

Некоторая идея в модульном тестировании кода C ++ в безымянном пространстве имен ... но суть в том, что инкапсуляция хороша. В конце концов, это та же проблема, что и у вас с закрытыми функциями-членами: они не могут быть протестированы ненавязчиво, но вы не хотите отказываться от скрытия информации для модульного тестирования (например, stackoverflow.com/a/3676680/3235496 ).
Манлио

1
Ваш случай концептуально не сильно отличается от случая тестирования (или не тестирования) частных методов. Поэтому, когда вы будете искать «модульный тест» здесь, в Программистах, вы получите множество ответов, которые вы можете применить непосредственно к вашему делу.
Док Браун

@DocBrown Я не спрашиваю, как проверить функции в ближайшее время. Пространства имен. Я спрашиваю "зачем помещать код в anon. Пространства имен?" (см. текст в конце вопроса). Виноват Иксрек за то, что сменил название на что-то другое.
Abyx

1
@Abyx: в этих других ответах, которые я упомянул выше, вы найдете здесь большое мнение многих экспертов о том, что тестировать частные методы - это действительно плохая идея, и что злоупотребление friendключевым словом для этой цели не рекомендуется. Сочетайте это с вашим предположением что если ограничение для метода приводит к ситуации, когда вы больше не можете тестировать его напрямую, это означает, что частные методы бесполезны.
Док Браун

Ответы:


7

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

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

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

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


6

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

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

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

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

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


5

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

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

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

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

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

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

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

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


Я хотел бы поднять это десять раз. Как касательная, связанная с критически важным, высоконадежным программным обеспечением, вы гораздо больше озабочены правильностью деталей. Этот идиоматический стиль для C ++ не подходит для этого. Я бы не использовал такие возможности языка, как анонимные пространства имен или шаблоны, похожие на прокси. Я бы грубо управлял своей границей с помощью [поддерживаемых] заголовков пространства пользователя и [неподдерживаемых] заголовков пространства разработчика.
Дэвид
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.