ccpputest

Faking values within "source file under test" from an unit test file


let's suppose I have a c source file (source file under test) with content:

uint8 reg1 = I2CRead(20)
uint8 reg2 = I2CRead(24)
uint8 reg3 = I2CRead(28)

if (reg1 != 0x10) || (reg2 != 0x11) || (reg3 != 0x12)
{
   register content wrong ...
} 

else
{
   register content is OK...
}

The I2CRead() function is constructed as follow:

uint8 I2CRead (uint8 address)
{
   I2C_Hw_Istance()->DTR = address  //DTR = Data Transmit Register

   return I2C_Hw_Instance()-> DRR   //DRR = Data Receive Register 
}

Now I'm trying to write a Unit Test (using cpputest framework) where I would like to "fake" the values read back by the function I2CRead() so that all equality expressions within the if() are fulfilled.

The Unit Test file is isolated from the "source file under test" but I can access on a special way the following function within the source file under test: I2C_Hw_Instance().

Using this function I can "fake" my register values within the Unit Test file on a way like this:

I2C_Hw_Istance()->DTR = 20;
I2C_Hw_Istance()->DRR = 0x10;

Because I have 3 I2CRead() function called successively I need to fake the DTR and DRR by each I2CRead() call individually. So this means the Unit test file needs to know when the next I2CRead() function is called to be able to manipulate the values in DTR and DRR.

How to do this in general, any idea?

My Idea was to have a kind of a fake file which is build together with the unit test (just "conceptual"):

FakeFile.c

void fakeRegisters()
{
   if first call of I2CRead() than:
     I2C_Hw_Istance()->DTR = 20;
     I2C_Hw_Istance()->DRR = 0x10;

   if second call of I2CRead()than:
     I2C_Hw_Istance()->DTR = 24;
     I2C_Hw_Istance()->DRR = 0x11;

   if tird call of I2CRead() than:
      I2C_Hw_Istance()->DTR = 28;
      I2C_Hw_Istance()->DRR = 0x12;
}

Solution

  • You need a choice of implementations of I2CRead():

    uint8_t I2CRead_i2c(uint8_t address)
    {
       I2C_Hw_Istance()->DTR = address  //DTR = Data Transmit Register
       return I2C_Hw_Instance()-> DRR   //DRR = Data Receive Register
    }
    
    
    uint8_t I2CRead_fake(uint8_t address)
    {
        switch (address) {
        case 20: return 0x10;
        case 24: return 0x11;
        case 28: return 0x12;
        }
        fail_test();                /* however you do this in your xUnit */
    }
    

    Now, in your code, you normally use the "real" implementation:

    uint8 (*I2CRead)(uint8) = I2CRead_i2c;
    

    When you run your tests, you need to inject your mock implementation:

    I2CRead = I2CRead_fake;
    

    This is one of the simplest fake implementations; there's a whole spectrum of fakery available to you if you need it (you might want to have an array containing your register contents so that you can test both the success and failure paths for starters).