ThrowTheSwitch / CMock

CMock - Mock/stub generator for C
http://throwtheswitch.org
MIT License
671 stars 273 forks source link

Missing _Expect methods #462

Closed jvieira88 closed 6 months ago

jvieira88 commented 1 year ago

Hi,

I'm using CMock with Ceedling for the first time. I'm just running a quick test to try out CMock but I'm not getting the same CMock methods (or signatures) as expected per the documentation. For example, the _Expect method seems to be missing.

Here's my system setup:

ceedling version
      Ceedling:: 0.31.1
      Unity:: 2.5.4
      CMock:: 2.5.4
      CException:: 1.3.3

Here are some code files:

DumbExample.h

#ifndef DUMBEXAMPLE_H_
#define DUMBEXAMPLE_H_

#include <stdint.h>

int8_t AverageThreeBytes(int8_t a, int8_t b, int8_t c);

#endif /* DUMBEXAMPLE_H_ */

DumbExample.c

#include "../inc/DumbExample.h"
#include "../inc/DumbBaseValue.h" //to test cmock

int8_t AverageThreeBytes(int8_t a, int8_t b, int8_t c)
{
    return (int8_t)(((int16_t)a + (int16_t)b + (int16_t)c + (int16_t)getBaseVal()) / 3);
}

DumbBaseValue.h

#ifndef INC_DUMBBASEVALUE_H_
#define INC_DUMBBASEVALUE_H_

#include <stdint.h>

int32_t getBaseVal (void);

#endif /* INC_DUMBBASEVALUE_H_ */

DumbBaseValue.c

#include "DumbBaseValue.h"

static int32_t iBaseVal = 0;

int32_t getBaseVal (void)
{
    return iBaseVal;
}

Generated mock_DumbBaseValue.h

/* AUTOGENERATED FILE. DO NOT EDIT. */
#ifndef _MOCK_DUMBBASEVALUE_H
#define _MOCK_DUMBBASEVALUE_H

#include "unity.h"
#include "DumbBaseValue.h"

/* Ignore the following warnings, since we are copying code */
#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)
#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))
#pragma GCC diagnostic push
#endif
#if !defined(__clang__)
#pragma GCC diagnostic ignored "-Wpragmas"
#endif
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wduplicate-decl-specifier"
#endif

void mock_DumbBaseValue_Init(void);
void mock_DumbBaseValue_Destroy(void);
void mock_DumbBaseValue_Verify(void);

#define getBaseVal_IgnoreAndReturn(cmock_retval) getBaseVal_CMockIgnoreAndReturn(__LINE__, cmock_retval)
void getBaseVal_CMockIgnoreAndReturn(UNITY_LINE_TYPE cmock_line, int32_t cmock_to_return);
#define getBaseVal_StopIgnore() getBaseVal_CMockStopIgnore()
void getBaseVal_CMockStopIgnore(void);
#define getBaseVal_ExpectAndReturn(cmock_retval) getBaseVal_CMockExpectAndReturn(__LINE__, cmock_retval)
void getBaseVal_CMockExpectAndReturn(UNITY_LINE_TYPE cmock_line, int32_t cmock_to_return);
typedef int32_t (* CMOCK_getBaseVal_CALLBACK)(int cmock_num_calls);
void getBaseVal_AddCallback(CMOCK_getBaseVal_CALLBACK Callback);
void getBaseVal_Stub(CMOCK_getBaseVal_CALLBACK Callback);
#define getBaseVal_StubWithCallback getBaseVal_Stub

#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)
#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))
#pragma GCC diagnostic pop
#endif
#endif

#endif

test_DumbExample.c

#include "unity.h"
#include "../inc/DumbExample.h"

#include "mock_DumbBaseValue.h"

void setUp(void)
{
    //Is run before every test, put unit init calls here
    mock_DumbBaseValue_Init();
}

void tearDown(void)
{
    //Is run after every test, put unit clean-up calls here
    mock_DumbBaseValue_Destroy();
}

void test_AverageThreeBytes_should_AverageMidRangeValues(void)
{
    getBaseVal_Expect(1);
    TEST_ASSERT_EQUAL_HEX8(40, AverageThreeBytes(30, 40, 50));
    TEST_ASSERT_EQUAL_HEX8(40, AverageThreeBytes(10, 70, 40));
    TEST_ASSERT_EQUAL_HEX8(33, AverageThreeBytes(33, 33, 33));
}

void test_AverageThreeBytes_should_AverageHighValues(void)
{
    getBaseVal_Expect(1);
    TEST_ASSERT_EQUAL_HEX8(80, AverageThreeBytes(70, 80, 90));
    TEST_ASSERT_EQUAL_HEX8(127, AverageThreeBytes(127, 127, 127));
    TEST_ASSERT_EQUAL_HEX8(84, AverageThreeBytes(0, 126, 126));
}

Error message: test/test_DumbExample.c:29:2: warning: implicit declaration of function 'getBaseVal_Expect'; did you mean 'getBaseVal_Stub'?

undefined reference to `getBaseVal_Expect'
collect2.exe: error: ld returned 1 exit status
ERROR: Shell command failed.

Any idea why this is happening? The _Expectmethod is not being generated in the mock file.

mvandervoord commented 1 year ago

The function getBaseVal has a return value, therefore it'll generate a getBaseVal_ExpectAndReturn() instead of a getBaseVal_Expect because if you're expecting it to get called, CMock also needs to know what it should return when the mock is called. :)

jvieira88 commented 1 year ago

Thanks for the clarification! That makes sense, I just assumed that all the mock methods would be created. I confirm it's working now :)

mvandervoord commented 1 year ago

Don't worry. This is often confusing for most people coming to CMock. I'm trying to think of a way to better prepare people for this behavior. It makes sense when it's brought up... but the "least surprising" behavior would be to still have an _Expect option. The problem if we DID also provide an _Expect function is that it would just return 0 because nothing was specified... and then people have a very hard time trying to figure out why their tests are failing.

Anyway, best of luck!

Letme commented 1 year ago

How about getting an Expect() function which would produce a compiler warning beside returning zero:

"Your function is expected to return a value, but you forgot to specify it using _ExpectAndReturn(). Returning 0 as default"
mvandervoord commented 1 year ago

I like this idea a lot. Anything that helps people find their own bugs is a win in my book. We could probably even simplify it down to a single call in cmock.c and just wrap it in macros for each function being tested, so that we keep the overhead down. :)

I'm reopening this bug, because I think this is 100% worth doing.

mvandervoord commented 6 months ago

Added the feature discussed above ❤️