Problem mocking with UT_PTR_SET

48 views
Skip to first unread message

Jakob Clausen

unread,
Feb 13, 2024, 8:38:03 AM2/13/24
to cpputest
Hi. I am struggling with replacing a real function with a mock.
I have two compilation units written in C, that I would like to test.
One of them uses the other one, and in that one I would like to switch between the original function and the mock during the test.
My two modules are mocked like this:

calculus.hpp:
#ifndef CALCULUS_MOCK_H
#define CALCULUS_MOCK_H

struct calculus {
    static double (*add)(double a, double b);
    static double (*subtract)(double a, double b);
    static void (*printstuff)();
};

extern "C"
{
    #include "../source/calculus.h"
}

namespace Fake
{
    double add(double x, double y);
    double subtract(double x, double y);
    void printstuff();
}

namespace Real
{
    double add(double x, double y);
    double subtract(double x, double y);
    void printstuff();
}

#endif

calculus.cpp:
#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"

#include "calculusmock.hpp"

#include <stdio.h>

namespace Real
{
    #include "../source/calculus.c"
}

namespace Fake
{
    double add(double x, double y)
    {
        printf("add mocked \n");

        return double(mock().actualCall(__func__)
            .withParameter("x", x)
            .withParameter("y", y)
            .returnDoubleValue());
    }

    double subtract(double x, double y)
    {
        printf("subtract mocked \n");

        return double(mock().actualCall(__func__)
            .withParameter("x", x)
            .withParameter("y", y)
            .returnDoubleValue());
    }

    void printstuff()
    {
        printf("printstuff mocked \n");

        mock().actualCall(__func__);
    }
}

/** Initialization of function pointers
 */
double (*calculus::add)(double a, double b) = Real::add;
double (*calculus::subtract)(double a, double b) = Real::subtract;
void (*calculus::printstuff)() = Real::printstuff;

/** Wrappers to be linked in place of original functions
 */
extern "C" double add(double a, double b)
{
    printf("add wrapped \n");
    return calculus::add(a, b);
}

extern "C" double subtract(double a, double b)
{
    printf("subtract wrapped \n");
    return calculus::subtract(a, b);
}

extern "C" void printstuff()
{
    printf("printstuff wrapped \n");
    return calculus::printstuff();
}

domath.hpp:
#ifndef DOMATH_MOCK_H
#define DOMATH_MOCK_H

struct domath {
    static double (*showmath)(double x, double y);
    static void* (*leak)();
    static void (*crash)();
};

extern "C"
{
    #include "../source/domath.h"
}

namespace Fake
{
    double showmath(double x, double y);
    void* leak();
    void crash();
}

namespace Real
{
    double showmath(double x, double y);
    void* leak();
    void crash();
}

#endif


domath.cpp:
#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"

#include "domathmock.hpp"
#include "calculusmock.hpp"

#include <stdio.h>

namespace Real
{
    #include "../source/domath.c"
}

namespace Fake
{
    double showmath(double x, double y)
    {
        printf("showmath mocked \n");

        return double(mock().actualCall(__func__)
            .withParameter("x", x)
            .withParameter("y", y)
            .returnDoubleValue());
    }

    void* leak()
    {
        printf("leak mocked \n");

        mock().actualCall(__func__);
    }

    void crash()
    {
        printf("crash mocked \n");

        mock().actualCall(__func__);
    }
}

/** Initialization of function pointers
 */
double (*domath::showmath)(double x, double y) = Real::showmath;
void* (*domath::leak)() = Real::leak;
void (*domath::crash)() = Real::crash;

/** Wrappers to be linked in place of original functions
 */

extern "C" double showmath(double x, double y)
{
    printf("showmath wrapped \n");
    return domath::showmath(x, y);
}

extern "C" void* leak() {
    return domath::leak();
}

extern "C" void crash() {
    return domath::crash();
}

When I run the following test, showmath() is called through the wrapper but printstuff does not. Why is that?

domathtest.cpp:

#include "CppUTest/TestHarness.h"
#include "CppUTestExt/MockSupport.h"

#include <stdio.h>

#include "../mock/calculusmock.hpp"
#include "../mock/domathmock.hpp"

TEST_GROUP(math)
{
    void setup()
    {
        UT_PTR_SET(domath::showmath, Real::showmath);
        UT_PTR_SET(domath::leak, Real::leak);
        UT_PTR_SET(domath::crash, Real::crash);

        UT_PTR_SET(calculus::add, Fake::add);
        UT_PTR_SET(calculus::subtract, Fake::subtract);
        UT_PTR_SET(calculus::printstuff, Fake::printstuff);
    }

    void teardown()
    {
        mock().clear();
    }

};

TEST(math, domath)
{
    printf("domath \n");

    mock().expectNCalls(2, "printstuff");
    printstuff();

    mock().expectOneCall("add")
          .withParameter("x", 25.0)
          .withParameter("y", 25.0)
          .andReturnValue(5.0f);

    mock().expectOneCall("subtract")
          .withParameter("x", 35.0)
          .withParameter("y", 35.0)
          .andReturnValue(15.0f);

    // act
    double sum = showmath(25, 25);

    // assert
    DOUBLES_EQUAL(5, sum, 0.01);

    mock().checkExpectations();
}

TEST(math, leak)
{
    //leak();
}

TEST(math, crash)
{
    //crash();
}

My test main looks like this:
#include "CppUTest/CommandLineTestRunner.h"

int main(int ac, char** av)
{
MemoryLeakWarningPlugin::turnOnThreadSafeNewDeleteOverloads();
//MemoryLeakWarningPlugin::turnOffNewDeleteOverloads();
    return CommandLineTestRunner::RunAllTests(ac, av);
}


James W Grenning

unread,
Feb 13, 2024, 9:24:53 AM2/13/24
to cpputest

Hi Jakob

There is a lot to digest.

Does this build?
Does it run but in an unexpected way?
Do you have other mocks you have built that work?
Can you boil it down to a simple example?

I think there is an easier way to do it.

Can you start by sharing that small portion of the production "../source/calculus.h?

--
You received this message because you are subscribed to the Google Groups "cpputest" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cpputest+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/cpputest/e7944a2d-fa8f-4e07-9519-7cbe31a66391n%40googlegroups.com.


Jakob Clausen

unread,
Feb 13, 2024, 10:04:14 AM2/13/24
to cpputest
Yes it does.
I have not included all the code. Can I attach a project folder here?

The output of the test is as follows. printstuff is called in the test, and it is wrapped and mocked as it should
showmathc is wrapped and not mocked as it should, and it in turn calls printstuff again, which is not mocked
I would expect it to be.
The test then fails because I have an unrealistic expectation value.
So at the start of the test I call

        UT_PTR_SET(domath::showmath, &Real::showmath);
        UT_PTR_SET(domath::leak, &Real::leak);
        UT_PTR_SET(domath::crash, &Real::crash);

        UT_PTR_SET(calculus::add, &Fake::add);
        UT_PTR_SET(calculus::subtract, &Fake::subtract);
        UT_PTR_SET(calculus::printstuff, &Fake::printstuff);

So showmath is not mocked. It calls printstuff, which should be mocked.
Does that make sense?
The entire project consists of two header-source pairs, two corresponding header-source-mocks and two testfiles.
And then a makefile.

domath
printstuff wrapped
printstuff mocked
showmath wrapped
What to do with the two numbers 25.000000 and 25.000000
stuff
We can add them 50.000000
we can subtract them 0.000000
This time we add them

test/domathtest.cpp:50: error: Failure in TEST(math, domath)
expected <5>
but was  <50> threshold used was <0.01>

.subtract wrapped
.add wrapped
.
Errors (1 failures, 3 tests, 3 ran, 6 checks, 0 ignored, 0 filtered out, 1 ms)

make[1]: *** [makefile:47: application_testrunner] Error 1

Jakob Clausen

unread,
Feb 13, 2024, 10:04:55 AM2/13/24
to cpputest
So I guess my problem is: how do I use UT_PTR_SET with these namespaces?

Jakob Clausen

unread,
Feb 13, 2024, 10:08:42 AM2/13/24
to cpputest
Sorry. You asked about production code. It looks like this:

calculus.h
#ifndef CALCULUS1_H
#define CALCULUS1_H


double add(double x, double y);
double subtract(double x, double y);
void printstuff();

#endif //CALCULUS1_H

domath.h
#ifndef DOMATH_H
#define DOMATH_H


double showmath(double x, double y);
void* leak();
void crash();

#endif //DOMATH_H

calculus.c
#include "calculus.h"
#include <stdio.h>


double add(double x, double y)
{
    return x+y;

}

double subtract(double x, double y)
{
    return x-y;
}

void printstuff()
{
    printf("stuff \n");
}

domath.c
#include "domath.h"
#include "calculus.h"

#include <stdio.h>
#include <stdlib.h>


double showmath(double x, double y)
{
    printf("What to do with the two numbers %f and %f \n", x, y);

    printstuff();

    double sum = add(x, y);
    printf("We can add them %f \n", sum);

    double difference = subtract(x, y);
    printf("we can subtract them %f \n", difference);

    printf("This time we add them\n");
    return sum;
}

void* leak()
{
    return malloc(1);
}

void crash()
{
    int* pointer;
    *pointer = 100;
}


James W Grenning

unread,
Feb 13, 2024, 10:11:48 AM2/13/24
to cpputest

It runs and builds, good.

Can you remove or comment out parts to get to a simple example, doing one thing.

Jakob Clausen

unread,
Feb 13, 2024, 10:20:39 AM2/13/24
to cpputest
Yes I can.
Now I have a test that calls printstuff, which gets mocked, and the it calls domath, which is not mocked, which in turn calls printstuff that is somehow also not mocked.
Which parts do you want to see?

Jakob Clausen

unread,
Feb 14, 2024, 4:13:06 AM2/14/24
to cpputest
Now I figured it out.
A long nights sleep seemed to do it. :-)
I used the same namespace names in two different mocks. Don't do that.
Now it works.
Thank you very much. :-)

Reply all
Reply to author
Forward
0 new messages