[嵌入式TDD实战]TDD开发AT26DF驱动模块(四):继续重构继续码

前言

上一章中,我们详细的讲解了在用TDD的方式写完一个接口时的各个步骤。这一章中,我们继续写下一个,并体会下在有测试保障下代码重构,优化质量。

继续码

低频ReadArray

那既然已经写好了08那个ReadArray了,那就把03那个也顺带写了吧。

03这个是只用于低频的,和08的唯一区别就是不用多写那个随便什么字节。

所以写出来差不多:

AT26DF.h

……
#define AT26DF_OPC_READARRAY          0x0B
#define AT26DF_OPC_READARRAY_LOWFREQ  0x03
……
// description: Sequentially read a continuous stream of data begun from the address specified.
// parameter  : addr   the starting address
//              buf    buffer to store the result.
//              len    the length of data you want to read.
// return     : 
// note       : this function can be used for lower frequency read operations up to the maximum 
//              specified by fRDLF
void AT26DF_readArrayLowFreq(AT26DF_ADDR addr, uint8_t *buf, uint16_t len);

AT26DFTest.c

……
TEST(AT26DF, ReadArrayLowFreq){
  int i;
  AT26DF_ADDR startAddr = 0x123456;
  const uint8_t expectData[] = {0x12, 0x34, 0x56, 0x78, 0x9a};
  uint8_t actualData[sizeof(expectData)];
  const uint8_t len = sizeof(expectData);
  SPI_SelectCS_Expect();
  SPI_writeByte_Expect(AT26DF_OPC_READARRAY_LOWFREQ);
  // address byte
  SPI_writeByte_Expect(0x12);
  SPI_writeByte_Expect(0x34);
  SPI_writeByte_Expect(0x56);
  for(i = 0; i < len; i++){
    SPI_readByte_ExpectAndReturn(expectData[i]);
  }
  SPI_DeselectCS_Expect();

  AT26DF_readArrayLowFreq(startAddr,actualData,len);

  TEST_ASSERT_EQUAL_HEX8_ARRAY(expectData,actualData,len);
}
……

AT26DFTestRunner.c

#include "unity_fixture.h"

TEST_GROUP_RUNNER(AT26DF)
{
  RUN_TEST_CASE(AT26DF, ReadArray);
  RUN_TEST_CASE(AT26DF, ReadArrayLowFreq);
}

AT26DF.c

……
void AT26DF_readArrayLowFreq(AT26DF_ADDR addr, uint8_t *buf, uint16_t len){
  _csSel();
  _spiWrite(AT26DF_OPC_READARRAY_LOWFREQ);
  _spiWrite((addr >> 16) & 0xFF);
  _spiWrite((addr >> 8) & 0xFF);
  _spiWrite(addr & 0xFF);
  _spiBurstRead(buf,len);
  _csDesel();
}

测试也很顺利的通过了:

但他和高频的那个几乎就一个样子嘛。所以我们要开始去冗余了。

重构

首先,在测试中,把冗余的 写地址Expect、公用的数组给提出来,AT26DFTest.c就成了这个样:

#include <string.h>
#include "unity_fixture.h"
#include "AT26DF.h"
#include "MockSPI.h"

TEST_GROUP(AT26DF);
static const uint8_t anyByte = 0xEE;
static int i;
static uint8_t expectData[] = {0x12, 0x34, 0x56, 0x78, 0x9a};
static uint8_t actualData[sizeof(expectData)];
static const uint8_t len = sizeof(expectData);
static void _WriteAddrExpect(AT26DF_ADDR addr){
  SPI_writeByte_Expect(((addr >> 16) & 0xFF));
  SPI_writeByte_Expect(((addr >> 8) & 0xFF));
  SPI_writeByte_Expect((addr & 0xFF));
}
static void _WriteDummyByteExpect(void){
  SPI_writeByte_Expect(anyByte);
  SPI_writeByte_IgnoreArg_b();
}
TEST_SETUP(AT26DF){
  MockSPI_Init();
  AT26DF_regFuncSPI(SPI_readByte,SPI_writeByte);
  AT26DF_regFuncCS(SPI_SelectCS,SPI_DeselectCS);
  for(i = 0; i < len; i++)
    ++expectData[i];
  memset(actualData,0,len);
}

TEST_TEAR_DOWN(AT26DF){
  MockSPI_Verify();
  MockSPI_Destroy();
}

TEST(AT26DF, ReadArray){
  AT26DF_ADDR startAddr = 0x345254;
  SPI_SelectCS_Expect();
  SPI_writeByte_Expect(AT26DF_OPC_READARRAY);
  _WriteAddrExpect(startAddr);
  _WriteDummyByteExpect();
  for(i = 0; i < len; i++)
    SPI_readByte_ExpectAndReturn(expectData[i]);
  SPI_DeselectCS_Expect();

  AT26DF_readArray(startAddr,actualData,len);

  TEST_ASSERT_EQUAL_HEX8_ARRAY(expectData,actualData,len);
}
TEST(AT26DF, ReadArrayLowFreq){
  AT26DF_ADDR startAddr = 0x123456;
  SPI_SelectCS_Expect();
  SPI_writeByte_Expect(AT26DF_OPC_READARRAY_LOWFREQ);
  _WriteAddrExpect(startAddr);
  for(i = 0; i < len; i++)
    SPI_readByte_ExpectAndReturn(expectData[i]);
  SPI_DeselectCS_Expect();

  AT26DF_readArrayLowFreq(startAddr,actualData,len);

  TEST_ASSERT_EQUAL_HEX8_ARRAY(expectData,actualData,len);
}

甚至为了expectData不重复,我还在每个Setup时修改其值,并重置actualData的值。这样可以避免测试之间相互影响,出现一个测试的通过导致另一个测试的初始值就通过的现象。
还抽象了_WriteDummyByteExpect方法,使可读性更好。

这时记得运行下测试,保证没改出问题。

同样的,.c文件中也可以做类似的去冗余,我们把写地址给抽象出来,毕竟这是很多操作都要用到的:

……
static void _writeAddr(AT26DF_ADDR addr){
  _spiWrite((uint8_t)(addr >> 16));
  _spiWrite((uint8_t)(addr >> 8));
  _spiWrite((uint8_t)addr);
}

void AT26DF_readArray(AT26DF_ADDR addr, uint8_t *buf, uint16_t len){
  _csSel();
  _spiWrite(AT26DF_OPC_READARRAY);
  _writeAddr(addr);
  _spiWrite(DONTCAREBYTE);
  _spiBurstRead(buf,len);
  _csDesel();
}

void AT26DF_readArrayLowFreq(AT26DF_ADDR addr, uint8_t *buf, uint16_t len){
  _csSel();
  _spiWrite(AT26DF_OPC_READARRAY_LOWFREQ);
  _writeAddr(addr);
  _spiBurstRead(buf,len);
  _csDesel();
}

再次运行测试。

通过。

添加参数检查

很好,但是好像还有点瑕疵,我们只测了正常的情况,要是参数有问题怎么办?

我们的readArray的参数中,addr无所谓,反正只认第几位得了。buf最有可能出问题,必须要检查NULL的情况。然后len为0的时候其实应该直接返回,不应该做任何事对吧,反正没有读任何字节,为什么要浪费CPU去做任何事情?

我们为这些异常情况写一个测试用例,就叫Read0LenOrBufNULLDoNothing:

……
static const AT26DF_ADDR anyAddr = 0x122534;
……
TEST(AT26DF, Read0LenOrBufNULLDoNothing){
  uint8_t buf[1];
  AT26DF_readArray(anyAddr,buf,0);
  AT26DF_readArrayLowFreq(anyAddr,buf,0);
  AT26DF_readArray(anyAddr,NULL,1);
  AT26DF_readArrayLowFreq(anyAddr,NULL,1);
}

记得到Runner中去安装新测试用例。

一运行测试,果然报错。测试那个NULL的则是程序直接崩溃:

参数检查添加起来也简单,两个接口都加这两行就得了

……
void AT26DF_readArray(AT26DF_ADDR addr, uint8_t *buf, uint16_t len){
  if(buf == NULL || len == 0)
    return;
  _csSel();
  ……

再次运行测试。通过。

很好,现在我们的两个接口都有了基本的检查,还去除了明显的冗余。

在CW上验证下吧

我们已经写了这么多代码了,该到CW上测试下了。

打开CW,我们把新加的mock文件和接口定义文件加进去

然后点击运行:

很好,没有发现平台差异导致的问题。如果你愿意,还可以接上开发板在实际芯片上进行验证。

在进行嵌入式TDD双目标平台开发时,偶尔地我们要到仿真器或真机上跑一下测试。因为虽然不常见,偶尔还是会出现因为平台差异导致PC上成功的测试,到仿真器或真机上就失败的情况。

图:嵌入式TDD循环

结语

这一章中,我们又实现了一个接口,并对测试和实现代码都进行了重构。实际上,整个嵌入式TDD的流程都已经展示的差不多了,接下来我们加把劲写完整个模块。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值