新聞中心
在軟件開發(fā)中,程序自測(cè)是一個(gè)永遠(yuǎn)都繞不開的話題。很多開發(fā)人員以寫出有難度的代碼為榮,但卻不重視對(duì)自己編寫的代碼進(jìn)行測(cè)試,這導(dǎo)致了最終到達(dá)客戶手中的產(chǎn)品質(zhì)量不高,bug頻發(fā),損害了公司的形象。對(duì)于一個(gè)開發(fā)人員來(lái)說(shuō),我們應(yīng)該將開發(fā)和自測(cè)置于同等重要的地位,我們花在自測(cè)上的時(shí)間要不比開發(fā)少。能否對(duì)自己編寫的代碼進(jìn)行充分的自測(cè)也是檢驗(yàn)一個(gè)開發(fā)人員水平高低的標(biāo)準(zhǔn)之一。

創(chuàng)新互聯(lián)主營(yíng)興賓網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,app軟件定制開發(fā),興賓h5小程序設(shè)計(jì)搭建,興賓網(wǎng)站營(yíng)銷推廣歡迎興賓等地區(qū)企業(yè)咨詢
自測(cè)方法
根據(jù)所編寫的程序的特點(diǎn),自測(cè)方法大致有如下幾種:
***種,利用模擬工具進(jìn)行自測(cè)。這種方法適用于需要其他模塊(尚不具備)發(fā)過來(lái)的消息才能觸發(fā)程序流程的情況。模擬工具要嚴(yán)格按照協(xié)議的要求發(fā)消息,并處理相應(yīng)的應(yīng)答消息。這種方法的優(yōu)點(diǎn)是可模擬真實(shí)的系統(tǒng)來(lái)測(cè)試代碼,功能覆蓋比較完全;其缺點(diǎn)是模擬工具的編寫比較復(fù)雜(相當(dāng)于要實(shí)現(xiàn)一個(gè)完整的功能模塊)、花費(fèi)的時(shí)間較多。
第二種,利用對(duì)端模塊進(jìn)行自測(cè)。這種方法適用于兩個(gè)軟件模塊功能的耦合性比較強(qiáng)且在同時(shí)開發(fā)的情況。例如,正在開發(fā)的模塊1的功能A需要正在開發(fā)的模塊2的功能B才能觸發(fā),而模塊1和模塊2的開發(fā)進(jìn)度差不多,此時(shí),就可以通過模塊2向模塊1發(fā)消息的方法來(lái)對(duì)功能A和功能B進(jìn)行自測(cè)。
第三種,手動(dòng)插入數(shù)據(jù)或執(zhí)行命令進(jìn)行自測(cè)。這種方法適用于某個(gè)軟件模塊的功能比較獨(dú)立的情況。此時(shí),沒有其他模塊與該模塊進(jìn)行消息的交互,也沒有編寫單獨(dú)的測(cè)試模塊。利用手工的方法的優(yōu)點(diǎn)是可以根據(jù)程序的特點(diǎn)設(shè)置測(cè)試用例,代碼覆蓋率比較高;其缺點(diǎn)是難以對(duì)大數(shù)據(jù)量的消息進(jìn)行測(cè)試,無(wú)法保證程序性能。
第四種,在程序中添加測(cè)試代碼進(jìn)行自測(cè)。也就是說(shuō),在編寫軟件功能代碼的同時(shí),將測(cè)試代碼一并加入其中。這種方法的優(yōu)點(diǎn)比較明顯,那就是無(wú)需與其他程序模塊進(jìn)行消息交互、無(wú)需手動(dòng)插入數(shù)據(jù)或發(fā)消息,當(dāng)整個(gè)軟件模塊運(yùn)行起來(lái)之后,測(cè)試代碼即可發(fā)揮其功效。當(dāng)然,該方法也有缺點(diǎn),那就是編寫這些測(cè)試代碼需要花費(fèi)一定的時(shí)間,可能導(dǎo)致代碼過于臃腫。
本文接下來(lái)的部分,將用實(shí)際的C代碼來(lái)示例第四種自測(cè)方法的使用。
示例程序
本文用到的示例程序比較簡(jiǎn)單,其功能是每個(gè)一段時(shí)間(1分鐘)將源目錄中滿足前綴要求的文件備份(移動(dòng))到備份目錄中。
程序(FilesBackup.c)如下:
- /**********************************************************************
- * 版權(quán)所有(C)2016, Zhou Zhaoxiong。
- *
- * 文件名稱:FilesBackup.c
- * 文件標(biāo)識(shí):無(wú)
- * 內(nèi)容摘要:將某個(gè)目錄中的文件進(jìn)行備份
- * 其它說(shuō)明:無(wú)
- * 當(dāng)前版本:V1.0
- * 作 者:Zhou Zhaoxiong
- * 完成日期:20160701
- *
- **********************************************************************/
- #include
- #include
- #include
- #include
- #include
- // 重定義數(shù)據(jù)類型
- typedef signed int INT32;
- typedef unsigned int UINT32;
- typedef unsigned char UINT8;
- // 全局變量定義
- UINT8 g_szSourceDir[256] = {0}; // 源文件的目錄
- UINT8 g_szBackupDir[256] = {0}; // 備份文件的目錄
- // 函數(shù)聲明
- INT32 SelectFlies(struct dirent *pDir);
- void ScanDirAndBackup(void);
- void Sleep(UINT32 iCountMs);
- /****************************************************************
- * 功能描述: 主函數(shù)
- * 輸入?yún)?shù): 無(wú)
- * 輸出參數(shù): 無(wú)
- * 返回值: 0-執(zhí)行完成
- * 其他說(shuō)明: 無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *-------------------------------------------------------------
- * 20160701 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ****************************************************************/
- INT32 main(void)
- {
- INT32 iRetValue = 0;
- // 獲取源文件的目錄
- snprintf(g_szSourceDir, sizeof(g_szSourceDir)-1,"%s/zhouzx/TestDir/SourceDir", getenv("HOME"));
- // 獲取備份文件的目錄
- snprintf(g_szBackupDir, sizeof(g_szBackupDir)-1,"%s/zhouzx/TestDir/BackupDir", getenv("HOME"));
- // 調(diào)用函數(shù)執(zhí)行文件的備份
- while (1)
- {
- ScanDirAndBackup();
- Sleep(60 * 1000); // 每一分鐘執(zhí)行一次文件的備份
- }
- return 0;
- }
- /**********************************************************************
- * 功能描述:根據(jù)前綴和后綴選擇文件
- * 輸入?yún)?shù):dir-目錄
- * 輸出參數(shù):無(wú)
- * 返回值:0-失敗 1-成功
- * 其它說(shuō)明:無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *--------------------------------------------------------------------
- * 20160701 V1.0 ZhouZhaoxiong 創(chuàng)建
- ***********************************************************************/
- INT32 SelectFlies(struct dirent *pDir)
- {
- INT32 iSelectResult = 0;
- UINT8 szFilePrefix[10] = {0}; // 源文件的前綴
- if (pDir == NULL)
- {
- printf("SelectFlies:input parameter is NULL!\n");
- return 0;
- }
- // 匹配文件前綴和后綴
- strncpy(szFilePrefix, "File_", strlen("File_"));
- iSelectResult = (strncmp(pDir->d_name, szFilePrefix,strlen(szFilePrefix)) == 0);
- if (iSelectResult == 1) // 找到了匹配前綴的文件
- {
- return 1;
- }
- else
- {
- return 0;
- }
- }
- /**********************************************************************
- * 功能描述:掃描目錄并備份文件
- * 輸入?yún)?shù):無(wú)
- * 輸出參數(shù):無(wú)
- * 返回值:無(wú)
- * 其它說(shuō)明:無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *--------------------------------------------------------------------
- * 20160701 V1.0 ZhouZhaoxiong 創(chuàng)建
- ***********************************************************************/
- void ScanDirAndBackup(void)
- {
- INT32 iScanDirRet = 0;
- UINT32 iFileIdx = 0;
- UINT8 szScanedFile[512] = {0};
- UINT8 szCmdBuf[256] = {0};
- struct dirent **ppDirEnt = NULL;
- iScanDirRet = scandir(g_szSourceDir, &ppDirEnt, SelectFlies,alphasort);
- if (iScanDirRet < 0) // 掃描目錄出錯(cuò)
- {
- printf("ScanDirAndBackup:exec scandir failed, path=%s\n",g_szSourceDir);
- return;
- }
- else if (iScanDirRet == 0) // 目錄下無(wú)文件
- {
- printf("ScanDirAndBackup:no satisfied file in directory %s\n",g_szSourceDir);
- return;
- }
- else // 將滿足條件的文件移動(dòng)到備份目錄中
- {
- for (iFileIdx = 0; iFileIdx < iScanDirRet; iFileIdx ++)
- {
- memset(szScanedFile, 0x00, sizeof(szScanedFile));
- snprintf(szScanedFile, sizeof(szScanedFile) - 1, "%s/%s",g_szSourceDir, ppDirEnt[iFileIdx]->d_name);
- memset(szCmdBuf, 0x00, sizeof(szCmdBuf));
- snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "mv %s %s",szScanedFile, g_szBackupDir);
- system(szCmdBuf);
- printf("ScanDirAndBackup:now, %s\n", szCmdBuf);
- }
- }
- printf("ScanDirAndBackup:this time,totally moved %d file(s) to%s\n", iScanDirRet, g_szBackupDir);
- return;
- }
- /**********************************************************************
- * 功能描述:程序休眠
- * 輸入?yún)?shù):iCountMs-休眠時(shí)間(單位:ms)
- * 輸出參數(shù):無(wú)
- * 返回值:無(wú)
- * 其它說(shuō)明:無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *------------------------------------------------------------------
- * 20160701 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ********************************************************************/
- void Sleep(UINT32 iCountMs)
- {
- struct timeval t_timeout = {0};
- if (iCountMs < 1000)
- {
- t_timeout.tv_sec = 0;
- t_timeout.tv_usec = iCountMs * 1000;
- }
- else
- {
- t_timeout.tv_sec = iCountMs /1000;
- t_timeout.tv_usec = (iCountMs % 1000) * 1000;
- }
- select(0, NULL, NULL, NULL, &t_timeout); // 調(diào)用select函數(shù)阻塞程序
- }
添加測(cè)試代碼之后的程序
我們添加測(cè)試代碼的基本思路是在掃描源文件目錄之前,先在該目錄下生成文件,這樣就相當(dāng)于手動(dòng)將文件放到源目錄中了。
添加之后的程序代碼如下:
- /**********************************************************************
- * 版權(quán)所有(C)2016, Zhou Zhaoxiong。
- *
- * 文件名稱:FilesBackup.c
- * 文件標(biāo)識(shí):無(wú)
- * 內(nèi)容摘要:將某個(gè)目錄中的文件進(jìn)行備份
- * 其它說(shuō)明:無(wú)
- * 當(dāng)前版本:V1.0
- * 作 者:Zhou Zhaoxiong
- * 完成日期:20160701
- *
- **********************************************************************/
- #include
- #include
- #include
- #include
- #include
- // 重定義數(shù)據(jù)類型
- typedef signed int INT32;
- typedef unsigned int UINT32;
- typedef unsigned char UINT8;
- // 全局變量定義
- UINT8 g_szSourceDir[256] = {0}; // 源文件的目錄
- UINT8 g_szBackupDir[256] = {0}; // 備份文件的目錄
- // 函數(shù)聲明
- INT32 SelectFlies(struct dirent *pDir);
- void ScanDirAndBackup(void);
- void Sleep(UINT32 iCountMs);
- void CreateTestFile(void);
- /****************************************************************
- * 功能描述: 主函數(shù)
- * 輸入?yún)?shù): 無(wú)
- * 輸出參數(shù): 無(wú)
- * 返回值: 0-執(zhí)行完成
- * 其他說(shuō)明: 無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *-------------------------------------------------------------
- * 20160701 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ****************************************************************/
- INT32 main(void)
- {
- INT32 iRetValue = 0;
- // 獲取源文件的目錄
- snprintf(g_szSourceDir, sizeof(g_szSourceDir)-1,"%s/zhouzx/TestDir/SourceDir", getenv("HOME"));
- // 獲取備份文件的目錄 snprintf(g_szBackupDir,sizeof(g_szBackupDir)-1, "%s/zhouzx/TestDir/BackupDir",getenv("HOME"));
- // 調(diào)用函數(shù)執(zhí)行文件的備份
- while (1)
- {
- // -------------
- // 先在源目錄中創(chuàng)建測(cè)試文件
- CreateTestFile();
- // -------------
- ScanDirAndBackup();
- Sleep(60 * 1000); // 每一分鐘執(zhí)行一次文件的備份
- }
- return 0;
- }
- /**********************************************************************
- * 功能描述:根據(jù)前綴和后綴選擇文件
- * 輸入?yún)?shù):dir-目錄
- * 輸出參數(shù):無(wú)
- * 返回值:0-失敗 1-成功
- * 其它說(shuō)明:無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *--------------------------------------------------------------------
- * 20160701 V1.0 ZhouZhaoxiong 創(chuàng)建
- ***********************************************************************/
- INT32 SelectFlies(struct dirent *pDir)
- {
- INT32 iSelectResult = 0;
- UINT8 szFilePrefix[10] = {0}; // 源文件的前綴
- if (pDir == NULL)
- {
- printf("SelectFlies:input parameter is NULL!\n");
- return 0;
- }
- // 匹配文件前綴和后綴
- strncpy(szFilePrefix, "File_", strlen("File_"));
- iSelectResult = (strncmp(pDir->d_name, szFilePrefix,strlen(szFilePrefix)) == 0);
- if (iSelectResult == 1) // 找到了匹配前綴的文件
- {
- return 1;
- }
- else
- {
- return 0;
- }
- }
- /**********************************************************************
- * 功能描述:掃描目錄并備份文件
- * 輸入?yún)?shù):無(wú)
- * 輸出參數(shù):無(wú)
- * 返回值:無(wú)
- * 其它說(shuō)明:無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *--------------------------------------------------------------------
- * 20160701 V1.0 ZhouZhaoxiong 創(chuàng)建
- ***********************************************************************/
- void ScanDirAndBackup(void)
- {
- INT32 iScanDirRet = 0;
- UINT32 iFileIdx = 0;
- UINT8 szScanedFile[512] = {0};
- UINT8 szCmdBuf[256] = {0};
- struct dirent **ppDirEnt = NULL;
- iScanDirRet = scandir(g_szSourceDir, &ppDirEnt, SelectFlies,alphasort);
- if (iScanDirRet < 0) // 掃描目錄出錯(cuò)
- {
- printf("ScanDirAndBackup:exec scandir failed, path=%s\n",g_szSourceDir);
- return;
- }
- else if (iScanDirRet == 0) // 目錄下無(wú)文件
- {
- printf("ScanDirAndBackup:no satisfied file in directory %s\n",g_szSourceDir);
- return;
- }
- else // 將滿足條件的文件移動(dòng)到備份目錄中
- {
- for (iFileIdx = 0; iFileIdx < iScanDirRet; iFileIdx ++)
- {
- memset(szScanedFile, 0x00, sizeof(szScanedFile));
- snprintf(szScanedFile, sizeof(szScanedFile) - 1, "%s/%s", g_szSourceDir,ppDirEnt[iFileIdx]->d_name);
- memset(szCmdBuf, 0x00, sizeof(szCmdBuf));
- snprintf(szCmdBuf, sizeof(szCmdBuf) - 1, "mv %s %s",szScanedFile, g_szBackupDir);
- system(szCmdBuf);
- printf("ScanDirAndBackup:now, %s\n", szCmdBuf);
- }
- }
- printf("ScanDirAndBackup:this time,totally moved %d file(s) to%s\n", iScanDirRet, g_szBackupDir);
- return;
- }
- /**********************************************************************
- * 功能描述:程序休眠
- * 輸入?yún)?shù):iCountMs-休眠時(shí)間(單位:ms)
- * 輸出參數(shù):無(wú)
- * 返回值:無(wú)
- * 其它說(shuō)明:無(wú)
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *------------------------------------------------------------------
- * 20160701 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ********************************************************************/
- void Sleep(UINT32 iCountMs)
- {
- struct timeval t_timeout = {0};
- if (iCountMs < 1000)
- {
- t_timeout.tv_sec = 0;
- t_timeout.tv_usec = iCountMs * 1000;
- }
- else
- {
- t_timeout.tv_sec = iCountMs /1000;
- t_timeout.tv_usec = (iCountMs % 1000) * 1000;
- }
- select(0, NULL, NULL, NULL, &t_timeout); // 調(diào)用select函數(shù)阻塞程序
- }
- /**********************************************************************
- * 功能描述:創(chuàng)建本地測(cè)試文件
- * 輸入?yún)?shù):無(wú)
- * 輸出參數(shù):無(wú)
- * 返回值:無(wú)
- * 其它說(shuō)明:每一輪創(chuàng)建的測(cè)試文件數(shù)目加1,到達(dá)1000個(gè)之后又從1開始
- * 修改日期 版本號(hào) 修改人 修改內(nèi)容
- *--------------------------------------------------------------------
- *20160701 V1.0 Zhou Zhaoxiong 創(chuàng)建
- **********************************************************************/
- void CreateTestFile(void)
- {
- UINT32 iFileIdx = 0;
- UINT8 szFileName[500] = {0};
- FILE *fp = NULL;
- static UINT32 s_iFileNo = 0;
- s_iFileNo ++;
- if (s_iFileNo > 1000)
- {
- s_iFileNo = 0;
- }
- for (iFileIdx = 0; iFileIdx < s_iFileNo; iFileIdx ++)
- {
- // 獲取帶路徑的文件名
- memset(szFileName, 0x00, sizeof(szFileName));
- snprintf(szFileName, sizeof(szFileName)-1, "%s/File_%d.txt",g_szSourceDir, iFileIdx);
- fp = fopen(szFileName, "a+");
- if (fp == NULL)
- {
- printf("CreateTestFile: open file %s failed!\n", szFileName);
- return;
- }
- fputs("Hello,world!", fp);
- fflush(fp);
- fclose(fp);
- fp = NULL;
- }
- if (s_iFileNo % 10 == 0) // 每生成10批滿足前綴要求的文件之后, 生成1個(gè)不滿足前綴要求的文件
- {
- memset(szFileName, 0x00, sizeof(szFileName));
- snprintf(szFileName, sizeof(szFileName)-1, "%s/Test_%d.txt",g_szSourceDir, s_iFileNo);
- fp = fopen(szFileName, "a+");
- if (fp == NULL)
- {
- printf("CreateTestFile: open file %s failed!\n", szFileName);
- return;
- }
- fputs("Hello,world!", fp);
- fflush(fp);
- fclose(fp);
- fp = NULL;
- }
- }
程序說(shuō)明如下:
***,本程序中添加的測(cè)試函數(shù)是CreateTestFile,其作用是在源目錄中創(chuàng)建測(cè)試文件??紤]到程序性能,我們?cè)O(shè)定最多生成1000(可根據(jù)實(shí)際情況修改)個(gè)滿足前綴要求的文件,并且每一輪生成的文件數(shù)比上一輪多一個(gè)。同時(shí),每生成10輪的滿足前綴要求的文件之后,要生成一個(gè)不滿足前綴要求的文件,用以測(cè)試異常情況。如此,正常和異常情況都考慮到了。
第二,示例代碼中寫入文件的內(nèi)容是固定的“Hello,world!”,大家可以根據(jù)需要修改該內(nèi)容以滿足自身測(cè)試的要求。
第三,在提交正式版本的時(shí)候,大家要將測(cè)試代碼注釋掉或刪掉,以免影響正常的程序流程。
總結(jié)
很多人所理解的軟件開發(fā)人員的工作就是寫代碼,而不包括測(cè)試,這樣的理解是片面的。實(shí)際的經(jīng)驗(yàn)表明,很多時(shí)候,我們花在測(cè)試上的時(shí)間比寫代碼的時(shí)間還要多。為了保證產(chǎn)品質(zhì)量,很多項(xiàng)目組也對(duì)自測(cè)提出了較高的要求。
作為一位合格的軟件開發(fā)人員,自測(cè)是一個(gè)檢驗(yàn)和提升自身能力的好方法,大家一定要對(duì)自己編寫的代碼進(jìn)行充分的測(cè)試。通過不斷地實(shí)踐,大家也可以總結(jié)出更多和更好的自測(cè)方法。
【本文是專欄作者周兆熊的原創(chuàng)文章,作者微信公眾號(hào):周氏邏輯(logiczhou)】
新聞名稱:軟件開發(fā)中的自測(cè)及C代碼示例
標(biāo)題URL:http://m.5511xx.com/article/dhjscso.html


咨詢
建站咨詢
