Я знаю, что это спорная практика, но давайте предположим, что это лучший вариант для меня. Мне интересно, какова реальная техника, чтобы сделать это. Подход, который я вижу, заключается в следующем:
1) Сделайте класс друга из класса, метод которого я хочу проверить.
2) В другом классе создайте открытый (ые) метод (ы), который вызывает закрытый (ые) метод (ы) тестируемого класса.
3) Проверьте общедоступные методы класса друга.
Вот простой пример, иллюстрирующий вышеуказанные шаги:
#include <iostream>
class MyClass
{
friend class MyFriend; // Step 1
private:
int plus_two(int a)
{
return a + 2;
}
};
class MyFriend
{
public:
MyFriend(MyClass *mc_ptr_1)
{
MyClass *mc_ptr = mc_ptr_1;
}
int plus_two(int a) // Step 2
{
return mc_ptr->plus_two(a);
}
private:
MyClass *mc_ptr;
};
int main()
{
MyClass mc;
MyFriend mf(&mc);
if (mf.plus_two(3) == 5) // Step 3
{
std::cout << "Passed" << std::endl;
}
else
{
std::cout << "Failed " << std::endl;
}
return 0;
}
Редактировать:
Я вижу, что в обсуждении после одного из ответов люди задаются вопросом о моей кодовой базе.
В моем классе есть методы, которые вызываются другими методами; ни один из этих методов не должен вызываться вне класса, поэтому они должны быть закрытыми. Конечно, их можно объединить в один метод, но по логике они гораздо лучше разделены. Эти методы достаточно сложны, чтобы гарантировать модульное тестирование, и из-за проблем с производительностью мне, скорее всего, придется пересмотреть эти методы, поэтому было бы неплохо провести тест, чтобы убедиться, что мой рефакторинг ничего не сломал. Я не единственный, кто работает в команде, хотя я единственный, кто работает над этим проектом, включая тесты.
Сказав выше, мой вопрос не был о том, является ли хорошей практикой написание модульных тестов для частных методов, хотя я ценю обратную связь.
источник
Ответы:
Альтернативой другу (в некотором смысле), которую я часто использую, является шаблон, который я узнал как access_by. Это довольно просто:
Теперь предположим, что класс B участвует в тестировании A. Вы можете написать это:
Затем вы можете использовать эту специализацию access_by для вызова закрытых методов A. По сути, это означает, что бремя объявления дружбы помещается в заголовочный файл класса, который хочет вызывать частные методы A. Это также позволяет добавлять друзей в A без изменения источника A. Идиоматически, это также указывает тому, кто читает источник A, что A не указывает B на истинного друга в смысле расширения его интерфейса. Скорее, интерфейс A завершен, как дано, и B требуется специальный доступ к A (хороший пример - тестирование, я также использовал этот шаблон при реализации привязок форсированного Python, иногда функция, которая должна быть закрытой в C ++, удобна для выставить в слой Python для реализации).
источник
friend access_by
, чтобы сделать первый не-друг достаточным - будучи вложенной структурой, он имел бы доступ ко всему внутри A? например. coliru.stacked-crooked.com/a/663dd17ed2acd7a3Если сложно проверить, плохо написано
Если у вас есть класс с частными методами, достаточно сложными, чтобы гарантировать собственный тест, класс делает слишком много. Внутри есть другой класс, пытающийся выбраться.
Извлеките закрытые методы, которые вы хотите протестировать, в новый класс (или классы) и сделайте их общедоступными. Проверьте новые классы.
Помимо упрощения тестирования кода, этот рефакторинг облегчит понимание и сопровождение кода.
источник
Вы не должны тестировать частные методы. Период. Классы, использующие ваш класс, заботятся только о методах, которые он предоставляет, а не о тех, которые он использует для работы.
Если вы беспокоитесь о покрытии кода, вам нужно найти конфигурации, которые позволят вам протестировать этот закрытый метод с помощью одного из открытых вызовов метода. Если вы не можете этого сделать, какой смысл иметь метод во-первых? Это просто недоступный код.
источник
Есть несколько вариантов сделать это, но имейте в виду, что они (по сути) изменяют открытый интерфейс ваших модулей, чтобы дать вам доступ к внутренним деталям реализации (эффективно превращая модульное тестирование в тесно связанные клиентские зависимости, где вы должны иметь нет зависимости вообще).
Вы можете добавить объявление друга (класса или функции) в тестируемый класс.
Вы можете добавить
#define private public
в начало ваших тестовых файлов, прежде чем#include
-проверенный код. Если тестируемый код является уже скомпилированной библиотекой, это может привести к тому, что заголовки больше не будут соответствовать уже скомпилированному двоичному коду (и вызовут UB).Вы можете вставить макрос в тестируемый класс и позднее решить, что означает этот макрос (с другим определением для тестирования кода). Это позволит вам тестировать внутренние компоненты, но также позволит стороннему клиентскому коду взломать ваш класс (путем создания своего собственного определения в объявлении, которое вы добавляете).
источник
Вот сомнительное предложение для сомнительного вопроса. Мне не нравится связь с другом, так как выпущенный код должен знать о тесте. Ответ Нира - один из способов облегчить это, но мне все еще не очень нравится менять класс, чтобы он соответствовал тесту.
Поскольку я не часто использую наследование, я иногда просто защищаю закрытые методы, а тестовый класс наследует и выставляет по мере необходимости. Реальность такова, что открытый API и API тестирования могут отличаться и все же отличаться от частного API, что оставляет вас в некотором роде.
Вот практический пример, к которому я прибегаю к этому трюку. Я пишу встроенный код, и мы очень полагаемся на конечные автоматы. Внешний API не обязательно должен знать о состоянии внутреннего конечного автомата, но тест должен (возможно) проверять соответствие диаграмме конечного автомата в проектной документации. Я мог бы выставить получатель «текущего состояния» как защищенный, а затем дать ему доступ к тестированию, что позволило бы мне более полно протестировать конечный автомат. Я часто нахожу этот тип класса трудным для тестирования как черный ящик.
источник
Вы можете написать свой код с множеством обходных путей, чтобы вам не приходилось пользоваться друзьями.
Вы можете писать классы и никогда не иметь никаких частных методов вообще. Все, что вам нужно сделать, - это создать функции реализации внутри модуля компиляции, позволить вашему классу вызывать их и передавать любые элементы данных, к которым они имеют доступ.
И да, это будет означать, что вы можете изменить сигнатуры или добавить новые методы «реализации», не меняя заголовок в будущем.
Вы должны взвесить, стоит ли это того или нет. И многое будет зависеть от того, кто увидит ваш заголовок.
Если я использую стороннюю библиотеку, я бы предпочел не видеть объявления друзей своим тестировщикам. Я тоже не хочу создавать их библиотеку и запускать их тесты. К сожалению, слишком много сторонних библиотек с открытым исходным кодом, которые я создал, делают это.
Тестирование - это работа авторов библиотеки, а не ее пользователей.
Однако не все классы видны пользователю вашей библиотеки. Многие классы являются «реализацией», и вы реализуете их наилучшим образом, чтобы обеспечить их правильную работу. В них у вас могут быть частные методы и члены, но вы хотите, чтобы юнит-тестеры их тестировали. Так что продолжайте и делайте так, если это приведет к более быстрому созданию надежного кода, что будет легко поддерживать для тех, кому это необходимо.
Если все пользователи вашего класса находятся в вашей собственной компании или команде, вы также можете немного больше расслабиться об этой стратегии, если она допускается стандартами кодирования вашей компании.
источник