1. 程式人生 > >【數獨個人專案】ConsolePatameter類的單元測試

【數獨個人專案】ConsolePatameter類的單元測試

5. 測試階段

ConsolePatameter類的單元測試

      用的vs自帶的單元測試,在解決方案資源管理器裡新建“本機單元測試“專案,將測試程式碼寫入,(不是自動測試)需要手寫測試測試程式碼和用例進去。。

1. 寫測試程式碼和用例:2小時

測試用例設計:

    黑盒測試,(白盒的話分支太多了。。不過儘管是黑盒,設計用例的時候也考慮到了程式中的分支,想盡可能覆蓋所有的分支)。要測試的有:

①. 常規合法輸入用例

(順序為:argc,argv陣列,GetCommand()返回值、GetOperationcode_c()返回值、GetOperationcode_s()返回值)

         {3, {"exename", "-c", "100"}, 'c', 100, "\0"},
         {3, {"exename", "-s", "mypath"}, 's', -1, "mypath"},

②. 邊界條件

        {3, {"exename", "-c", "-1"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "0"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "1"}, 'c', 1, "\0"},
        {3, {"exename", "-c", "2"}, 'c', 2, "\0"},
        {3, {"exename", "-c", "999999"}, 'c', 999999, "\0"},
        {3, {"exename", "-c", "1000000"}, 'c', 1000000, "\0"},
        {3, {"exename", "-c", "1000001"}, '\0', -1, "\0"},

③. 其他的,儘可能能覆蓋所有分支的所有用例

argc不為3的:

        {1, {"exename" }, '\0', -1, "\0"},
        {2, {"exename", "-c"}, '\0', -1, "\0"},
        {2, {"exename", "aaaaaaaaaaaaaaaaa"}, '\0', -1, "\0"},
        {4, {"exename", "-ccc", "12345", "12345"}, '\0', -1, "\0"},

-c且後面引數不合法的

        {3, {"exename", "-c", "ccccc"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "123a"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "&&&&"}, '\0', -1, "\0"},

-s且後面引數不合法的
        {3, {"exename", "-s", "&&my&&path"}, 's', -1, "&&my&&path"},
        {3, {"exename", "-s", "12345"}, 's', -1, "12345"},

根本就不是-c或者-s的
        {3, {"exename", "123", "12345"}, '\0', -1, "\0"},
        {3, {"exename", "-ccc", "12345"}, '\0', -1, "\0"},
        {3, {"exename", "-sss", "12345"}, '\0', -1, "\0"},
        {4, {"exename", "-ccc", "12345", "12345"}, '\0', -1, "\0"},

2. 問題和解決方法:

1. 一開始模組和控制檯輸入有關係,沒法單元測試。。必須得是獨立的模組才能測試,於是把模組加了個介面,把這個模組和控制檯分開了,類的輸入引數多了argc和argv,而不是直接呼叫控制檯的argc和argv

2. 還有個問題就是要不要測試private成員變數,(因為有個void的初始化函式,改變了private成員變數,如果要測試的話,需要加個#define private public)。後來想了下應該不用,因為當時測試的是ConsoleParameter這個類。只要類提供的公有方法的輸入輸出符合預期就夠了,不用管內部實現。當然更細的測試的話就會有這個問題了,比如測試類中的特定一個函式(void Init()函式)

3. 更改測試錯誤:1小時

測試出錯,只有錯誤資訊,沒找到測試程式的控制檯輸出在哪??難道只能除錯看值麼?

這樣的話甚至哪個用例出錯都看不出來的啊。。應該分成多個用例麼?那每個用例複製一遍測試程式碼?那樣太麻煩了吧。。我看一個測試用例只能輸出一個通過/不通過(比如我的這20個用例,輸出的就是一個通過)

不過後來把錯誤弄好了,是測試程式內部的錯誤,指標的問題,不是模組的錯誤

4. 測試通過

 

5. 測試程式碼分支覆蓋率

因為用visual studio 17 企業版好像有bug,網上的解決方法很少,試過了也沒法解決這個bug:

https://social.microsoft.com/Forums/zh-CN/f794a986-3511-44f6-b696-c93338175a93/vs2017-202251999429256c-3903330446-21333208032797935797?forum=vstudiozhchs

之後嘗試了安裝OpenCppCoverage外掛,也沒安裝成功。。也出錯了。。

// 2018-12-30,用大佬的vs裡的OpenCppCoverage外掛,測了下分支覆蓋率,結果證明。。和下面設想的基本一致

// 詳情見 https://blog.csdn.net/qq_37571192/article/details/85412919,整合測試程式碼分支覆蓋率分析

 

所以只能手工分析下程式碼分支覆蓋率。。

①. 常規合法輸入用例

②. 邊界條件

③. 儘可能能覆蓋所有分支的所有用例

1. argc不為3的:

2.  argc為3:

-c且後面引數不合法的 / 合法的

-s且後面引數不合法的 / 合法的

根本就不是-c或者-s的

 

其中③的測試用例就是根據程式碼的選擇分支條件來設計的,選擇的分支覆蓋率已經達到了100%,已經覆蓋全部分支

所以可以確保測試用例的覆蓋的全面性,能覆蓋所有分支,所以能覆蓋基本上所有程式碼

 

附:測試程式碼:

#include "stdafx.h"
#include "CppUnitTest.h"
#include <iostream>
#include "C:\Users\cky\source\repos\Project6\Project6\ConsoleParameter.h"

#define CASE_AMOUNT 21

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace UnitTestForConsolePatameter {
  TEST_CLASS(UnitTest1)
  {
  public:

    TEST_METHOD(Class_ConsoleParameter)
    {
      struct Testcase {
        int argc;
        char consoleinput[105][105];
        char expect_output_command;
        int expect_output_operationcode_c;
        string expect_output_operationcode_s;
      };
      struct Testcase testcase[CASE_AMOUNT] = {
        {1, {"exename" }, '\0', -1, "\0"},
        {2, {"exename", "-c"}, '\0', -1, "\0"},
        {2, {"exename", "aaaaaaaaaaaaaaaaa"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "100"}, 'c', 100, "\0"},
        {3, {"exename", "-s", "mypath"}, 's', -1, "mypath"},

        {3, {"exename", "-c", "-1"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "0"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "1"}, 'c', 1, "\0"},
        {3, {"exename", "-c", "2"}, 'c', 2, "\0"},
        {3, {"exename", "-c", "999999"}, 'c', 999999, "\0"},
        {3, {"exename", "-c", "1000000"}, 'c', 1000000, "\0"},
        {3, {"exename", "-c", "1000001"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "ccccc"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "123a"}, '\0', -1, "\0"},
        {3, {"exename", "-c", "&&&&"}, '\0', -1, "\0"},
        {3, {"exename", "-s", "&&my&&path"}, 's', -1, "&&my&&path"},
        {3, {"exename", "-s", "12345"}, 's', -1, "12345"},

        {3, {"exename", "123", "12345"}, '\0', -1, "\0"},
        {3, {"exename", "-ccc", "12345"}, '\0', -1, "\0"},
        {3, {"exename", "-sss", "12345"}, '\0', -1, "\0"},
        {4, {"exename", "-ccc", "12345", "12345"}, '\0', -1, "\0"},
      };
      for (int i = 0; i < CASE_AMOUNT; i++) {
        char *ptr[105] = { NULL };
        
        for (int j = 0; j < testcase[i].argc; j++) {
          ptr[j] = testcase[i].consoleinput[j];
        }
        char **argv = ptr;

        ConsoleParameter parameter;
        parameter.Init(testcase[i].argc, argv);

        Assert::AreEqual(testcase[i].expect_output_command, parameter.GetCommand());
        Assert::AreEqual(testcase[i].expect_output_operationcode_c, parameter.GetOperationcode_c());
        Assert::AreEqual(testcase[i].expect_output_operationcode_s, parameter.GetOperationcode_s());
      }
    }
  };
}