LINUX.ORG.RU

Кошерные unit тесты

 , ,


0

1

Добрый день, форумчане и уважаемые анонимусы.

Есть код:

class Statistics
{
};

class Validate{
    public:
    virtual bool IsValid( Statistics & statistics) {return false; }
};
    

class Protocol{
    protected:
    Validate* validate;
    public:
    virtual bool ValidateTheOutput(const std::string& output, Statistics & statistics, std::string& message) const = 0;
};

class SomeProtocol: public Protocol {    
    public:
    virtual bool ValidateTheOutput(const std::string& output, Statistics & statistics, std::string& message) const override
    {
        // собираем статистику
        // ....
        return validate->IsValid(statistics);
    }
};

требуется покрыть код unit тестами.

Вопрос как правильно проверить ValidateTheOutput?
В часности, как проверять:
return validate->Isvalid(statistics);

Чтобы не скатиться в функциональное тестирование.

Второй вопрос, как вы покрываете тестами приватные методы? Наследование?

У тебя ValidateTheOutput занимается двумя задачами минимум: сбор статистики и её валидация. Покрой тестами эти задачи по отдельности

XMs ★★★★★ ()

Ты точно уверен, что тебе не нужно тестировать только публичный интерфейс (так как приватные методы - это все же детали реализации)?

как вы покрываете тестами приватные методы? Наследование?

Можно еще попытаться как-то в тестовых сборках сделать твои тесты друзьями тестируемого класса. Вроде в гуглотестах было что-то вроде FRIEND_TEST.

Вопрос как правильно проверить ValidateTheOutput?

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

Makhno ()
Ответ на: комментарий от rust_afari

Rust мне нравится.
Говорят что где то есть кисельные берега с молочными реками, но в моей реальности стоя по колено в говнах в резиновых сапогах приходится работать с тем что есть ;).

unittests ()
Ответ на: комментарий от Makhno

Можно еще попытаться как-то в тестовых сборках сделать твои тесты друзьями тестируемого класса.

В моем случае вносить изменение в код и превращать в мешанину вида:

//g++ -DTEST 
class A{
#ifdef TEST
friend B;
#endif /*TEST*/
};

нельзя.

unittests ()
Ответ на: комментарий от XMs

занимается двумя задачами минимум

Это да.

но даже если сделать так:

virtual bool ValidateTheOutput(const std::string& output, Statistics & statistics, std::string& message) const override
    {
        //Валидируем внутренее состояние
        bool isValidState = false;
        // .....
        if (isValidState){
           return validate->IsValid(statistics);
        }
        return false;
    }

то вопросы как правильно проверить вызов валидатора остается.

unittests ()
Ответ на: комментарий от unittests

Можно собирать тесты с -Dprivate=public -Dprotected=public, но это то еще извращение. По-моему лучше сделать как-то так:

// Somewhere in util.h :)
#ifdef TEST
# define TEST_FRIENDLY friend class Test;
#else
# define TEST_FRIENDLY
#endif

// Usage:
class A {
    TEST_FRIENDLY
private:
....
public:
....
};

rymis ★★ ()

Просто добавляй fiend class test_foo_bar, без всяких #ifdef и тем более -Dprivate=public и -Dtrue=false

Deleted ()
Ответ на: комментарий от unittests

А в этом случае ты указываешь, что этот вызов сам не проверяется, а вместо него проверяются внутренние по отдельности. Либо, если у тебя найдётся такой набор входных данных, что по отдельности тесты проходят, а вывод — нет, делаешь отдельный тест по конкретному набору данных

XMs ★★★★★ ()

В часности, как проверять:

Проверяют инварианты и граничные условия, что ты тут собрался проверять то?

Второй вопрос, как вы покрываете тестами приватные методы?

friend class. А в нормальных ЯП доступ к приватным полям целиком соседям по пакету разрешают, чтобы не заниматься микроменеджментом и бойлерплейтостроительством.

snizovtsev ★★★★ ()
Ответ на: комментарий от unittests

то вопросы как правильно проверить вызов валидатора остается.

Тебе нужно проверить, что в тесте «валидатор вызвался»? Тогда:
man gmock
man dependency injection

Первое - это чтобы удобно сделать мок, можно и руками. Т.е. тебе нужно сделать «тупую» имплементацию интерфейса Valdate, которая выдаёт заданный в конструкторе ответ и сохраняет в себя историю вызовов. Это автоматизируется через макросы mock framework-а.

Второе - передавать этот класс в конструктор SomeProtocol. Т.е. нужно подрефакторить все классы так, чтобы они сами другие сущности не конструировали, а брали их из параметров конструктора (для удобства можно старое поведение оставить, если в конструктор передали nullptr). А всё конструирование (wiring) проводить в одном централизованном месте, и для тестов оно будет другое. Это называется dependency injection, тут тоже есть фреймворки, но они не так полезны как gmock и можно руками.

snizovtsev ★★★★ ()
Последнее исправление: snizovtsev (всего исправлений: 2)
Ответ на: комментарий от snizovtsev

Правда мало кто этим серьезно заморачивается, обычно сваливаются в функциональное тестирование. На крупный С++ проект я обычно вижу максимум 1-2 теста с моками, а обычно 0.

snizovtsev ★★★★ ()

как вы покрываете тестами приватные методы?

Паблик Морозов.

Но я не считаю, что покрывать приватные методы unit-тестами - хорошая идея.

trex6 ★★★★★ ()

Приватные методы должны быть выполнены при вызове публичных методов. Можно просто снимать покрытие и анализировать

vromanov ★★ ()
Ответ на: комментарий от Deleted

Для большого компьютера это очень просто. Ниже пример простейшего отчета сделанного при помощи gcov. Обычно генерится отчет в HTML и график с результатами покрытия на jenkins. Мы вот сейчас будем пытаться снимать покрытие с микроконтроллера.

File '/cygdrive/c/auto/common/utils/can_buffer.c'
Lines executed:100.00% of 76
Creating 'can_buffer.c.gcov'

File '/cygdrive/c/auto/common/utils/can_buffer_chain.c'
No executable lines
Removing 'can_buffer_chain.c.gcov'

File '/cygdrive/c/auto/common/utils/can_buffer_chains.c'
Lines executed:100.00% of 28
Creating 'can_buffer_chains.c.gcov'

File '/cygdrive/c/auto/common/utils/can_filter.c'
Lines executed:100.00% of 38
Creating 'can_filter.c.gcov'

File '/cygdrive/c/auto/common/utils/can_message.c'
Lines executed:100.00% of 39
Creating 'can_message.c.gcov'

File '/cygdrive/c/auto/common/utils/can_parse_dump.c'
Lines executed:36.17% of 47
Creating 'can_parse_dump.c.gcov'

File '/cygdrive/c/auto/common/utils/crc32.c'
Lines executed:100.00% of 35
Creating 'crc32.c.gcov'

vromanov ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.