Unit Testing: from theory to real case
Sujet : Unit Testing: from theory to real case
De : pozzugno (at) *nospam* gmail.com (pozz)
Groupes : comp.arch.embeddedDate : 27. Aug 2024, 12:52:21
Autres entêtes
Organisation : A noiseless patient Spider
Message-ID : <vakb55$2sp38$2@dont-email.me>
User-Agent : Mozilla Thunderbird
I read a lot about unit testing, but unfortunately I usually work on single-developer projects with stressing time constraints, so I never created full tests for an entire project in the past. This means I'm a newbie in this aspect of software development.
I know the importance of testing, but we have to admit that it increases the cost of software development a lot, at least at the beginning. Not always we have the possibility to invest this price.
Everytime I start writing some tests, I eventually think I'm wasting my precious time. Most probably because I'm not able to create valid tests.
So I'm asking you to help on a real case.
First of all, I have a great confusion in my mind about the subtle differences about mocks, stubs, fakes, dummies and so on. Anyway I think these names are not so important, so go on.
These days I'm working on a calendar scheduler module. The client of this module can configure up to N events that could be:
- single (one shot)
- weekly (for example, on Monday and Saturday of every weeks)
- monthly (for example, the days 3-5-15 of every months)
- yearly (for example, the day 7 of months Jan, Feb and Mar)
Weekly, monthly and yearly events have a starting time and *could* have a maximum number of repetitions (or they could be forever).
The interface is very simple. I have some functions to initialize the configuration of an event (a simple C struct):
void calev_config_init_single(CalendarEventConfig *config, time_t timestamp, CalendarEventActions *actions);
void calev_config_init_weekly(CalendarEventConfig *config, time_t timestamp, uint8_t weekdays, unsigned int nrep, CalendarEventActions *actions);
void calev_config_init_monthly(CalendarEventConfig *config, time_t timestamp, uint32_t mdays, unsigned int nrep, CalendarEventActions *actions);
void calev_config_init_yearly(CalendarEventConfig *config, time_t timestamp, uint16_t months, unsigned int nrep, CalendarEventActions *actions);
I have a function that initializes the module with some pre-programmed events:
void calendar_init(CalendarEventConfig *list_events, size_t num_events);
I have a function that is called every second that triggers actions on occurrences:
void calendar_task(void);
So, the client of calendar module usually does the following:
CalendarEventConfig events[4];
calev_config_init_...(&events[0], ...
calev_config_init_...(&events[1], ...
calev_config_init_...(&events[2], ...
calev_config_init_...(&events[3], ...
calendar_init(events, 4);
while(1) {
calendar_task(); // every second
...
}
The calendar module depends on some other modules. First of all, it asks for the current time as time_t. It calls make_actions() function, with certain parameters, when an event occurrence expired.
I know how to fake the time, replacing the system time with a fake time. And I know how to create a mock to check make_actions() calls and parameters.
Now the problem is... which tests to write?
I started writing some tests, but after completed 30 of them, I'm thinking my work is not valid.
I was tempted to write tests in this way:
TEST(TestCalendar, OneWeeklyEvent_InfiniteRepetition)
{
CalendarEventConfig cfg;
calev_config_init_weekly(&cfg, parse_time("01/01/2024 10:00:00"),
MONDAY | SATURDAY, 0, &actions);
set_time(parse_time("01/01/2024 00:00:00")); // It's monday
calendar_init(&cfg, 1);
set_time(parse_time("01/01/2024 10:00:00")); // First occurrence
mock().expectOneCall("make_actions")...
calendar_task();
set_time(parse_time("06/01/2024 10:00:00")); // It's saturday
mock().expectOneCall("make_actions")...
calendar_task();
set_time(parse_time("08/01/2024 10:00:00")); // It's monday again
mock().expectOneCall("make_actions")...
calendar_task();
mock().checkExpectations();
}
However it seems there are many sub-tests inside OneWeeklyEvent_InfiniteRepetition test (the first occurrence, the second and third).
The tests should have a single assertion and should test a very specific behaviour. So I split this test in:
TEST(TestCalendar, OneWeeklyEventInfiniteRepetition_FirstOccurrence)
TEST(TestCalendar, OneWeeklyEventInfiniteRepetition_SecondOccurrence)
TEST(TestCalendar, OneWeeklyEventInfiniteRepetition_ThirsOccurrence)
What else? When to stop?
Now for the weekly event with only 5 repetitions.
TEST(TestCalendar, OneWeeklyEvent5Repetitions_FirstOccurrence)
TEST(TestCalendar, OneWeeklyEvent5Repetition_SecondOccurrence)
TEST(TestCalendar, OneWeeklyEvent5Repetition_SixthOccurrence_NoActions)
The combinations and possibilities are very high. calendar_init() can be called with only 1 event, with 2 events and so on. And the behaviour for these cases must be tested, because it should behaves well with 1 event, but not with 4 events.
The events can be passed to calendar_init() in a random (not cronologically) order. I should test this behaviour too.
There could be one-shot, weekly with infinite repetitions, weekly with a few repetitions, monthly... yearly, with certain days in common...
calendar_init() can be called when the current time is over the starting timestamp of all events. In some cases, there could be future occurrences yet (infinite repetitions) and in others that event can be completely expired (limited repetitions).
I'm confused. How to scientifically approach this testing problem? How to avoid the proliferation of tests? Which tests are really important and how to write them?
Haut de la page
Les messages affichés proviennent d'usenet.
NewsPortal