CMock

80 views
Skip to first unread message

Aarush Jain

unread,
Mar 7, 2024, 1:27:50 PM3/7/24
to ThrowTheSwitch Forums
Is it possible to mock functions from header files that dont have a source file?

Best,
Aarush Jain

Mike Karlesky

unread,
Mar 7, 2024, 1:31:46 PM3/7/24
to throwth...@googlegroups.com

Absolutely.

In fact, mocks do not require a “real” source file at all. Mock generation occurs solely from header files.

This can enable a handful of useful things. Particularly in prototyping code or in true Test Driven Development, you can test production code you’re writing without having to write the definitions of any of the functions your code under test calls out to.


--
You received this message because you are subscribed to the Google Groups "ThrowTheSwitch Forums" group.
To unsubscribe from this group and stop receiving emails from it, send an email to throwtheswitc...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/throwtheswitch/b2497d22-0f0a-4d6d-8cef-5479cb127d36n%40googlegroups.com.

Aarush Jain

unread,
Mar 7, 2024, 1:41:10 PM3/7/24
to ThrowTheSwitch Forums
I am writing some test code for a NXP S32K144 project and I am able to mock functions from header files that have a source file and for one hardware abstraction file that doesnt have a source file, it is unable to do so. the following is the file in question. Thanks!

Best,
Aarush Jain

pins_gpio_hw_access.h

David Good

unread,
Mar 7, 2024, 1:49:40 PM3/7/24
to throwth...@googlegroups.com
Are you trying to use functions which are nested under the #ifdef xxxxxx sections and if so , are you defining the symbols required ?

Like Mike said , mocking headers without source files is very , very common practice , so this is something with your project / setup .

--David

Mark Vander Voord

unread,
Mar 7, 2024, 1:59:34 PM3/7/24
to throwth...@googlegroups.com
Thanks for providing a sample header. I suspect the problem is mostly related to most of the functions in that file being static inlines instead of actual functions. This gets into a tricky linking issue. Your header is providing the actual code as inlines, so it's challenging to mock it.

The latest Ceedling that is about to be released (0.32.xxx) has special handling for these situations where it will actually create a temporary header file that has the same interface, but is actually mockable. If you're on the previous version, (0.31 or earlier) I suspect that's why you're seeing problems.

Mark

Mike Karlesky

unread,
Mar 7, 2024, 2:01:30 PM3/7/24
to throwth...@googlegroups.com

Complex header files quite commonly break CMock. CMock does not include a full C parser. It's practically impossible to provide full C parsing in a cross-platform way, able to work with all variations of C, standardized and with proprietary extensions. CMock does its best with fairly powerful regular expressions and string manipulation.

Hardware definition header files are quite commonly too complex for CMock's built-in file scanning abilities.

You have options. I'll briefly list some of them here. I'd suggest searching through this forum for more details. This is a common question and many have helped with this need in the past.
  1. Use Ceedling with test preprocessing enabled. With this enabled, Ceedling expands header files with the GNU cpp tool, picks them apart, and then provides the “clean” header file to CMock. For similar reasons as spoken to above, this does not necessarily work in all cases.
  2. Write some version of a stand-in header file. Here, you write and maintain a subset of the header file you want to mock and name the file identically. This is only used for test builds. While, yes, there's duplication, it's quite uncommon for hardware definitions to change. And, typically, you only need a manageable subset of the functions in a stand-in header file. Everything will compile and link properly so long as you separate include search paths appropriately. A nice side benefit is that this acts as a kind of documentation as to what your code is dependent on.
  3. If you're using a proprietary toolchain, you can experiment with its preprocessing modes to roll your own version of (1). While this is hypothetically possible, it tends to be fairly complicated and relies on scripts and glue code that often make it impractical.
p.s. Incidentally, because CMock only mocks header files and because of how macros are processed by the preprocessor syntactically, you can, in fact, mock macros! So long as your macro has a parenthetical argument list, you can duplicate it as a function declaration in a header file that CMock then mocks. Under test, your source code then calls a mocked function instead of a macro. Again, if you keep your files separated with test vs. release search paths, this is entirely possible. In some special cases this can be valuable — e.g. legacy code with lots of macros or specialized hardware feature wrapper macros.


Aarush Jain

unread,
Mar 7, 2024, 2:14:58 PM3/7/24
to throwth...@googlegroups.com
Hi David,

I am attempting to mock the PINS_GPIO_ReadPins function which does have the #ifdef section. Would I define that in my test harness somewhere before I mock the function?

On Thu, Mar 7, 2024 at 1:49 PM David Good <david...@gmail.com> wrote:

Aarush Jain

unread,
Mar 7, 2024, 2:18:14 PM3/7/24
to throwth...@googlegroups.com
if I were to cross compile the test harness when I need to mock hardware header files on the MCU, would that negate the problem?

Best,
Aarush

Aarush Jain

unread,
Mar 7, 2024, 2:20:43 PM3/7/24
to throwth...@googlegroups.com
Hey Mark,

I mocked functions from the following pin_driver.h file and was able to do this successfully. While it may be a bit more high level, why is cmock able to mock this dependency rather than the gpio hardware file?

pins_driver.h

Mark Vander Voord

unread,
Mar 7, 2024, 2:30:20 PM3/7/24
to throwth...@googlegroups.com
You haven't specified exactly what error you're seeing when you attempt to mock the previous header. Looking inside both files, my suspicion is that they both mock properly, but then the original fails to compile or link (because the mock has a conflict with the static inline functions... either it's going to complain that those functions don't exist at all, or it's going to complain that they are redefined).

The pins_driver.h file doesn't have static inline functions, so cmock is able to handle them easily. All the functions in the headers are just declarations. When CMock attempts to mock it, it sees the declarations, and then creates a mock for all of them.

The gpio header has a lot of static inline functions. If CMock is in the mode to ignore static and/or inline functions, it will complain that it can't find anything to mock.

If CMock is in the mode to attempt to mock static and/or inline functions, it will attempt to do so... but then when you link to it, you get a conflict (there will be a mock version of those functions AND there is the original inlined version of those functions.

In either case, it fails.

The newer Ceedling & CMock attempt to work around these situations by producing a temporary header file which does NOT have inlines... and then it uses THAT as the header during your test instead of the original. This feature isn't perfect yet, but it often helps.

Mark

Aarush Jain

unread,
Mar 7, 2024, 2:56:09 PM3/7/24
to throwth...@googlegroups.com
Would it be possible to exclude this low level hardware abstraction from the mocking. I am testing a high level module which relies on a module chain all the way down to this one. If I only include up to the the high level abstraction in the mocking process, will my high level module still be testable? I'm asking this because when I excluded this from the mocking, I got a "Function. Called fewer times than expected" error which is what I previously got when I didnt mock the internal dependencies.

--
You received this message because you are subscribed to the Google Groups "ThrowTheSwitch Forums" group.
To unsubscribe from this group and stop receiving emails from it, send an email to throwtheswitc...@googlegroups.com.

Mark Vander Voord

unread,
Mar 7, 2024, 3:49:22 PM3/7/24
to throwth...@googlegroups.com
Yes. When you're mocking modules, you can ignore that any lower-level modules exist completely. The exception to this is when module you're testing sometimes skips a mocked module and makes calls directly to the layer below. Then you'd need to mock that level as well.

If you're working with functions with include inlines, this is further complicated because the inline function may/not get mocked depending on your version and settings. If it's not mocked, then any functions that the inline calls will need to exist (as mocks or as real code).

The error you're seeing, "called fewer times than expected", means that your test code said you were expecting a function to get called... but your test code isn't calling it. This can be either because your code is taking a different path than expected... and this actually is a problem... or it might be because of the inline problem mentioned earlier. If you have a function which has been inlined AND you also mocked it, your file under test might call the inline version, which means the mock never gets called (producing this error).

Is this error referring to one of your examples you mentioned previously? Which function is getting called fewer times than expected?

Mark

Aarush Jain

unread,
Mar 8, 2024, 3:32:47 PM3/8/24
to throwth...@googlegroups.com
Hey Mark,

I managed to solve the problem because as you mentioned, the module I was testing didn't directly call the low level hardware abstraction layers so all is good! Thanks a lot! Also, just a side note, for test functions, if i have a setup of mocked dependencies and i want to check multiple asserts for multiple values, can i do them in the same function or do i need to have another test function with the same mocked dependency setup? Thanks!


--
You received this message because you are subscribed to the Google Groups "ThrowTheSwitch Forums" group.
To unsubscribe from this group and stop receiving emails from it, send an email to throwtheswitc...@googlegroups.com.

Mark Vander Voord

unread,
Mar 8, 2024, 3:36:12 PM3/8/24
to throwth...@googlegroups.com
I'm glad you were able to get it working. :)

You can check as many assertions as you wish from a single test function. The test function will stop on the first failure and report it, skipping the rest of the function. If you want it to report ALL the failures, you would want to separate them. If you only care that none of them fail, stringing multiple assertions together makes good sense.

Mark

Aarush Jain

unread,
Mar 11, 2024, 1:59:55 PM3/11/24
to throwth...@googlegroups.com
Hi Mark,

Thank you so much for your help! 

Aarush Jain

unread,
Mar 11, 2024, 2:10:36 PM3/11/24
to throwth...@googlegroups.com
Hi Mark,

Thank you so much for your help! Is there a way to run ceedling on the target microcontroller with a cross compiler? Thanks!

Best Regards,

Aarush Jain

Mark Vander Voord

unread,
Mar 11, 2024, 2:13:20 PM3/11/24
to throwth...@googlegroups.com
Hi Aarush:

Yes, Ceedling can be used to run tests on the target itself, but honestly it's much better to run on a simulator or natively. Tests are rarely as complete when run on the target, and they test time gets much slower.

We actually wrote some advice on this topic, if you're interested in reading more:


Mark
Reply all
Reply to author
Forward
0 new messages