I want to test-drive functions in C that manipulate an array. But the solutions that I find make me feel somewhat uncomfortable.
Lets speak about the limitations first: I'm using a forward declared struct that is a pointer to a kind of structure my test should have no knowledge of (only my implementation knows the actual details of the struct). That is in order to make sure users, I or my tests will only interact with the list using the to be programmed functions.
The reason that makes me feel uncomfortable about my solution is the following:
I create a first test that checks the list is empty on creation. I have a TaskList_Create() function. "Bad enough" the memory is already initialized with 0. So I can't really make a difference between memory as it was and it being initialized 0 by TaskList_Create().
All this leads me to creating another function without having a test for it. Namely TaskList_AddTask().
#include "TaskList.h"
TEST_GROUP(TaskList)
{
TaskList tl;
void setUp(void)
{
tl = TaskList_Create(4); /* 4 elements in size */
}
void tearDown(void)
{
TaskList_Destroy(tl);
}
}
TEST(TaskList, Create_willInitializeListEmpty_v1)
{
TaskList_AddTask(tl); /* returns non-zero value (PID) */
TaskList_AddTask(tl); /* ... */
TaskList_AddTask(tl);
CHECK_EQUAL(4, TaskList_AddTask(tl));
CHECK_EQUAL(0, TaskList_AddTask(tl));
}
TEST(TaskList, Create_willInitializeListEmpty_v2)
{
CHECK_EQUAL(1, TaskList_IsEmpty(tl));
}
TEST(TaskList, Create_willInitializeListEmpty_v3)
{
CHECK_EQUAL(0, TaskList_TasksInstalled(tl));
}
I use TaskList_AddTask() on the created list N+1 times, where N is the size of the list. This would allow me to verify the list is empty after TaskList_Create(), as the last call to TaskList_AddTask() would return some sort of failure (for which I have no test yet ... urg!).
The pattern that arises during this kind of development is that I will always have to create a untested function that will help me test the actual function I wanted to test in the first place.
I create TaskList_AddTask() to actually test TaskList_Create() or I write TaskList_IsEmpty().
Another issue: I want to test TaskList_DeleteTask(), which makes me write TaskList_AddTask().
TEST(TaskList, DeleteTask)
{
uint8_t pid;
TaskList_AddTask(tl);
TaskList_AddTask(tl);
TaskList_AddTask(tl);
pid = TaskList_AddTask(tl);
TaskList_DeleteTask(tl, pid);
CHECK_EQUAL(4, TaskList_AddTask(tl));
}
Or: I want to test TaskList_Add() will find and recycle slots in the list that have previously been deleted, which forces me to write TaskList_DeleteTask().
In short: I don't feel comfortable having to write a 2nd function that is untested in order to test-drive another function.
I'm not exactly sure if faking/mocking would be of help her. Lets say, in order to test TaskList_DeleteTask() without having or using TaskList_AddTask() I'd have a FakeTaskList_Create() that I could pass a number that would specify the number of elements that should already be contained in the list, just in order to test my TaskList_DeleteTask() function.
I don't know too much about TDD-anti-patterns but what I'm doing right now sure feels like one.
Any, really any advice welcome!
FYI (but it should not matter):
typedef struct TaskStruct {
void (*fp)(void);
uint16_t periode;
uint16_t delay;
uint8_t run;
} TaskStruct;
typedef TaskList TaskStruct *;
tl = TaskList_Create(4)
?TaskList
.