Модульное тестирование в указателе области C

Я пытаюсь реализовать модульное тестирование на C с помощью Ceedling.

struct Person *list;
/* TASK_FIND_BY */
void test_main_task_find_by_normal(void)
{
    char *string = (char *)malloc(sizeof(char));
    struct Person new_list_instance;
    struct Person *new_list = &new_list_instance;
    ask_input_ExpectAndReturn(string);
    llist_find_by_ExpectAndReturn(list, string, new_list);
    llist_print_Expect(new_list);
    llist_remove_all_Expect(&new_list);

    printf("\nOutside single: %p\n", new_list);
    printf("Outside double: %p\n", &new_list);

    task_find_by(list);
}
void task_find_by(struct Person *list)
{
    char *input = NULL;
    struct Person *new = NULL;

    printf("Input exact name/surname/email/phone: ");
    input = ask_input();
    if (input == NULL) {
        printf("Error!");
        return;
    }
    new = llist_find_by(list, input);
    if (new != NULL)
        llist_print(new);
    else 
        printf("Address not found!\n");
    
    printf("\nInside single: %p\n", new);
    printf("Inside double: %p\n", &new);
    
    free(input);
    llist_remove_all(&new);
}

Я ожидаю, что функция внутри task_find_by будет вызываться с тем же аргументом, что и здесь: llist_remove_all_Expect(&new_list). Но фактическая функция вызывается с адресом указателя new, где new содержит new_list. Поскольку new_list и new — разные указатели, их адреса разные. Есть ли способ проверить вызываемый аргумент без изменения реализации функции task_find_by?

  - "Outside single: 0x7ffc2e3c03d0"
  - "Outside double: 0x7ffc2e3c03c0"
  - "Inside single: 0x7ffc2e3c03d0"
  - "Inside double: 0x7ffc2e3c0398"

Обратите внимание, что malloc(sizeof(char)) выделяют место для одного символа. Это может содержать только пустую строку, нуль-терминатор строки '\0', не более того.

Some programmer dude 09.05.2023 13:51

И, пожалуйста, попробуйте создать минимальный воспроизводимый пример, чтобы показать нам. Что это за llist_find_by_ExpectAndReturn «функция»? Это действительно макрос, определяющий переменную list? Что он тогда инициализирует как? Вы уверены, что это не будет делать что-то вроде list = new_list или подобного?

Some programmer dude 09.05.2023 13:57

@Someprogrammerdude malloc(sizeof(char)) предназначен только для получения адреса (достаточно одного байта) в куче из-за того, как CMock и моя функция ожидают получить адрес, и это совершенно не относится к делу, и это не делает что нибудь еще. И чтобы ответить на ваш второй комментарий, это макрос, созданный CMock, который представляет собой издевательский интерфейс, включенный в структуру модульного тестирования Ceedling, и весь код внутри test_main_task_find_by_normal(void) имеет отношение к этому вопросу. Обновлено: struct Person *list; определяется как глобальная переменная, которая ничего не содержит.

Mykolas D. 09.05.2023 14:35

Если результат этого макроса или функций, которые он вызывает, приводит к чему-то вроде list = new_list, то вы получите ожидаемый результат. Вы пытались запустить код через препроцессор, чтобы увидеть, во что он расширяется? Как насчет того, чтобы использовать отладчик, чтобы увидеть, что на самом деле происходит, и что действительно происходит, и каков может быть результат?

Some programmer dude 09.05.2023 14:53

@ryyker Я имею в виду llist_remove_all_Expect(&new_list), где он ожидает &new_list вызова, а в реальной функции он получает &new вызов, где new = new_list

Mykolas D. 09.05.2023 14:59

@Someprogrammerdude Да, это действительно ожидаемый результат, и поэтому я спрашиваю, есть ли способ фактически протестировать эту функциональность без изменения реализации функции, которая тестируется модулем. (Возможно, мне не хватает некоторых функций Ceedling или есть обходной путь для проверки адреса)

Mykolas D. 09.05.2023 15:01

Возможно, вам просто придется использовать llist_remove_all_ExpectAnyArgs();, поскольку невозможно узнать адрес автоматической переменной new'. Либо так, либо сделайте полный макет обратного вызова для этой функции, которая проверяет содержимое указателя.

pmacfarlane 09.05.2023 15:56

Извините, но я в замешательстве. Кажется, что вопрос говорит о том, что поведение отличается от того, что вы ожидали, но в комментариях вы, кажется, говорите, что это так, как ожидалось. Кроме того, мне неясно, что вы подразумеваете под «вызванным аргументом» или какой тест вы хотите выполнить на нем.

John Bollinger 09.05.2023 16:04

Плохо, я хотел сказать, что хочу, а не ожидать, и, поскольку это не работает так, как я хочу, я ищу решение. И я, вероятно, просто выберу решение @pmacfarlane

Mykolas D. 09.05.2023 16:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
96
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Поскольку вы не можете знать адрес автоматической переменной new, у вас в основном есть два варианта.

Игнорируйте параметр, который передается llist_remove_all() (но все равно проверяйте, вызывается ли он):

llist_remove_all_ExpectAnyArgs();

Или напишите функцию-заглушку, которая может проверять содержимое указателя, переданного в функцию. Что-то вроде:

static struct Person *expect_person;

static void llist_remove_all_my_stub(struct Person **p, int NumCalls)
{
    // Original suggestion
    //if (*p != expect_person)
    //    TEST_FAIL();

    // Better suggestion, from someone in comments
    TEST_ASSERT_EQUAL_PTR(expect_person, *p);
}

void test_main_task_find_by_normal(void)
{
    struct Person new_list_instance;
    ...
    expect_person = &new_list_instance;
    llist_remove_all_Stub(llist_remove_all_my_stub);
    llist_remove_all_Expect(&new_list);
    task_find_by(list);
}

В Unity из фреймворка Ceedling также существует TEST_ASSERT_EQUAL_PTR (ожидаемый_указатель, фактический_указатель), который должен улучшить ясность при тестировании рабочей области, если у кого-то еще есть эта проблема. И спасибо за хороший обходной путь!

Mykolas D. 10.05.2023 10:09

Я [хочу], чтобы функция внутри task_find_by вызывалась с тем же аргументом, что и здесь: llist_remove_all_Expect(&new_list).

Аргумент, который вы передаете последнему, имеет другой тип, чем параметр первого. Эти типы несовместимы, и если сравниваемые значения равны, то это почти наверняка указывает на ошибку в вашем тестовом наборе или в тестируемом коде.

Но фактическая функция вызывается с адресом указателя new, где new содержит new_list.

Если функция получает параметр, равный (struct Person *) &new, то что-то ужасно не так либо с тестами, либо с тестируемым кодом. В task_find_by()new обозначает локальную переменную. Единственный способ, которым его адрес может быть передан в эту функцию в качестве аргумента, — это либо если вызывающий объект использует указатель после окончания времени жизни его референта, либо если передается совершенно дикий указатель.

Возможно, вы имели в виду, что, передав &new_list в качестве аргумента llist_remove_all_Expect(), вы хотите передать new_list в task_find_by(). Это находится в пределах вашего непосредственного контроля.

Или, возможно, вы имели в виду, что хотите, чтобы llist_find_by_ExpectAndReturn(list, string, new_list) установил list равным new_list, в результате чего передача list в task_find_by() эквивалентна поведению, описанному в предыдущем абзаце. Это может быть связано с реализацией llist_find_by_ExpectAndReturn(), и если этого не происходит, то это может быть признаком того, что либо набор тестов, либо тестируемый код имеют недостатки.

Другие вопросы по теме