TDD on the C track - functions not under test have to be defined to compile and run the tests

I just finished the exercise Resistor Color in C on Exercism and found the TDD experience a bit frustrating. The exercise doesn’t provide function stubs so while the last test gets ignored:

the code won’t compile unless the color() function gets defined.

I also think that this error message isn’t very beginner friendly while this currently is the third exercise on the C track:

Compiling tests.out
In file included from ./test-framework/unity.h:21,
                 from ./test_resistor_color.c:1:
./test_resistor_color.c: In function ‘test_colors’:
./test_resistor_color.c:33:42: error: implicit declaration of function ‘colors’ [-Werror=implicit-function-declaration]
   33 |    TEST_ASSERT_EQUAL_INT_ARRAY(expected, colors(), ARRAY_LENGTH(expected));
      |                                          ^~~~~~
./test-framework/unity_internals.h:892:176: note: in definition of macro ‘UNITY_TEST_ASSERT_EQUAL_INT_ARRAY’
  892 |        UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT,     UNITY_ARRAY_TO_ARRAY)
      |                                                                                      ^~~~~~

./test_resistor_color.c:33:4: note: in expansion of macro ‘TEST_ASSERT_EQUAL_INT_ARRAY’
   33 |    TEST_ASSERT_EQUAL_INT_ARRAY(expected, colors(), ARRAY_LENGTH(expected));
      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~
./test-framework/unity_internals.h:892:155: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
  892 | nts, line, message)         UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT,     UNITY_ARRAY_TO_ARRAY)
      |                                                                                      ^

./test-framework/unity.h:295:100: note: in expansion of macro ‘UNITY_TEST_ASSERT_EQUAL_INT_ARRAY’
  295 | SSERT_EQUAL_INT_ARRAY(expected, actual, num_elements)                                UNITY_TEST_ASSERT_EQUAL_INT_ARRAY((expected), (actual), (num_elements), __LINE__, NULL)
      |                                                                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

./test_resistor_color.c:33:4: note: in expansion of macro ‘TEST_ASSERT_EQUAL_INT_ARRAY’
   33 |    TEST_ASSERT_EQUAL_INT_ARRAY(expected, colors(), ARRAY_LENGTH(expected));
      |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make: *** [makefile:37: tests.out] Error 1

I don’t know enough about C to propose an improvement. Is it possible to ignore this type of error during the TDD phase? What’s the reasoning for not including function stubs? Is it so people are forced to learn about header files?

Thank you for your work on the C track, looking forward to your responses and insights. :)

As you know there is a discussion in an issue on GitHub.

I’ve mentored this exercise on the C track more than a hundred times, I think I have a good idea where people struggle. The problems are:

  • The instructions are language-independent. They ask you “to create a way […] to look up the numerical value associated with a particular color band” and “to create a way […] to list the different band colors”. What that means is not really clear, you have to read the tests to find out.
    Sure, that’s not different from other exercises, but it can be a little bit difficult for beginners who are used to precise instructions in English and/or existing stubs that allow them just to focus on implementing functions. Now you have to read the tests, understand them, define the enumerators, declare and define the two functions.
  • If you’ve never heard of enum before you will have to do some research about how you can use it and about the implicit conversion from/to integer types.
  • The last test is for the function colors(), it requires that this function returns a pointer to the first element of an array (or a pointer to an fixed-sized array). For beginners and programmers coming from other languages that can be surprising and difficult to research on their own.
  • Similarly, the returned array cannot be a (non-static) local variable (because its lifetime ends with the function) nor a dynamically allocated variable (because there’s no free()). Again, you have to find out somehow that you need an array with static storage duration and that you can either use a global variable or a local static variable.

That’s quite a lot for an exercise that looks simple, comes early in the C track, and doesn’t provide any guidance.

But I also understand the other side.

  • enum is one of the fundamental constructs of C. If you struggle with it you will struggle even more with other problems.
  • At Exercism the tests are the specification. The instructions might provide some motivation and give a general direction, but the types, parameters, special cases are all in the tests. You need to read them for almost all exercises.
  • Returning a pointer is a pretty normal thing to do for C programmers. I’m not sure how we could make that more obvious.
  • Lifetime and ownership are essential considerations for C programmers, they do that all the time, and you have to start thinking about them for a lot of the other exercises, too.
  • Function declarations/definitions and the separation of the code into .h and .c files are fundamental for C code. If we would provide the declarations then the whole part of reading the tests, declaring the functions in the .h file, figuring out that the second function should return a pointer, would be given away.
    That might not necessarily bad, but it would alter the nature of this exercise fundamentally, and nobody has yet come up with a convincing and concrete idea.

I don’t know enough about C to propose an improvement. Is it possible to ignore this type of error during the TDD phase?

Currently no. For the tests to compile and get linked you will have to declare the functions in the .h file and define them. I agree, that’s unfortunate.
The C++ track does things a little bit differently, they use a preprocessor macro to hide disabled tests from the compiler. Perhaps we should think about that for that C track, too.

My own thoughts:

I think this problem could become a good concept exercise. With an explanation about enum the function color_code() would become easy to implement. Similarly, with an explanation about how to pass arrays to functions or return them, and how and when arrays decay to pointers, and about automatic, static, and allocated storage durations, the static array and a pointer as return type would become obvious.

I really hope that somebody (else) will find the time to create a syllabus

1 Like

First of all, thank you for your thorough and thoughtful response, was really helpful for my understanding. :slight_smile:

Do you think that a lot of the frustration of people comes from them not being used to this style of problem solving? I guess that will be less the case for tracks that have a syllabus since they would start out with exercises that include step by step instructions.

Would you consider adding info that’s hard to research for beginners to a

Ah yes, makes sense that a solution like that would be needed. I think it would be a nice improvement for the UX but also understand that someone has to find time and motivation to implement it.

That might be a good idea.
But I’d rather prefer turning this practice exercise into a concept exercise.

I think this exercise is odd. It doesn’t really pose a problem where you have to think about how you can solve it, if you know the building blocks (enum, implicit conversions, pointers/arrays, and lifetimes) the solution is obvious. An experienced programmer would probably solve it in two or three minutes without having to think about it. </rant>