Вот типичный код 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
и все остальное из анонимного пространства имен.
Вопрос: какой смысл помещать функции и константы в анонимное пространство имен, если это делает их непригодными для использования в тестах?
источник
foo.cpp
, а не заголовок! OP, кажется, прекрасно понимает, что не следует помещать анонимные пространства имен в заголовок.friend
ключевым словом для этой цели не рекомендуется. Сочетайте это с вашим предположением что если ограничение для метода приводит к ситуации, когда вы больше не можете тестировать его напрямую, это означает, что частные методы бесполезны.Ответы:
Если вы хотите выполнить модульное тестирование частных деталей реализации, вы делаете тот же вид уклонения для безымянных пространств имен, что и для частных (или защищенных) членов класса:
Перерыв и вечеринка.
В то время как для классов вы злоупотребляете
friend
, для безымянных пространств имен вы злоупотребляете#include
-механизмом, который даже не заставляет вас менять код.Теперь, когда ваш тест-код (или лучше всего что-то, чтобы разоблачить все) находится в том же TU, проблем нет.
Предупреждение: если вы тестируете детали реализации, ваш тест будет прерван, если они изменятся. Обязательно проверяйте только те детали реализации, которые все равно будут утекать, или примите, что ваш тест необычайно эфемерен.
источник
Функция в вашем примере выглядит довольно сложной, и может быть лучше переместить ее в заголовок для целей модульного тестирования.
Сделать их изолированными от остального мира. И это не только функции и константы, которые вы можете поместить в анонимное пространство имен - это также для типов.
Однако, если это делает ваши юнит-тесты очень сложными, то вы делаете это неправильно. В таком случае функция там не принадлежит. Затем пришло время немного изменить рефакторинг, чтобы упростить тестирование.
Таким образом, в анонимном пространстве имен должны идти только очень простые функции, иногда константы и типы (включая typedefs), используемые в этом модуле перевода.
источник
Код, который вы показали,
match
является довольно тривиальным 1-строчным без каких-либо хитрых краевых случаев, или это похоже на упрощенный пример? Во всяком случае, я предполагаю, что это упрощено ...Этот вопрос - то, что хотело заставить меня прыгнуть сюда, так как Deduplicator уже показал совершенно хороший способ взломать и получить доступ через
#include
обман. Но формулировка здесь звучит так, как будто тестирование каждой внутренней детали реализации всего является своего рода универсальной конечной целью, когда она далека от этого.Цель даже модульного тестирования не всегда состоит в том, чтобы протестировать каждый маленький гранулированный внутренний микроблок функциональности. Тот же вопрос относится к статическим функциям файловой области видимости в C. Вы можете даже сделать вопрос труднее ответить, спрашивая , почему разработчики используют
pimpls
в C ++ , который потребует какfriendship
и#include
фокусы в белой коробке, торгуясь легко тестируемости детали реализации для улучшения времени компиляции, напримерС прагматической точки зрения это может показаться грубым, но
match
может быть неправильно реализовано в некоторых крайних случаях, которые приводят к его сбою. Однако, если единственный внешний класс,Foo
который имеет доступ к нему,match
не может использовать его таким образом, чтобы он сталкивался с этими граничными случаями, то это довольно не имеет значения для правильности того,Foo
чтоmatch
эти граничные случаи никогда не встретятся, если не произойдутFoo
изменения, и в этот момент испытанияFoo
не пройдут, и мы сразу узнаем.Более навязчивый образ мыслей, жаждущий проверить каждую внутреннюю деталь реализации (возможно, критически важное программное обеспечение, например), может захотеть вмешаться и повеселиться, но многие люди не обязательно думают, что это лучшая идея, так как это создаст самые хрупкие испытания, которые только можно представить. YMMV. Но я просто хотел обратиться к формулировке этого вопроса, которая звучит так, как будто такая тестируемость на уровне мелких деталей должна быть конечной целью, когда даже самый строгий подход к юнит-тестированию может немного расслабиться. и избегайте рентгеновских снимков каждого класса.
Итак, почему люди определяют функции в анонимных пространствах имен в C ++ или как статические функции в области файлов с внутренней связью в C, скрытые от внешнего мира? И это в основном так: скрывать их от внешнего мира. Это имеет ряд последствий от сокращения времени компиляции до уменьшения сложности (то, что не доступно в другом месте, не может вызвать проблемы в другом месте) и так далее. Вероятно, проверяемость деталей частной / внутренней реализации не является первоочередной задачей для людей, когда они делают это, скажем, сокращая время сборки и скрывая ненужную сложность от внешнего мира.
источник