Google C++單元測試框架---Gtest框架簡介(譯文)

来源:http://www.cnblogs.com/jycboy/archive/2016/11/12/6057677.html
-Advertisement-
Play Games

一、設置一個新的測試項目 在用google test寫測試項目之前,需要先編譯gtest到library庫並將測試與其鏈接。我們為一些流行的構建系統提供了構建文件: msvc/ for Visual Studio, xcode/ for Mac Xcode, make/ for GNU make,  ...


一、設置一個新的測試項目

  在用google test寫測試項目之前,需要先編譯gtest到library庫並將測試與其鏈接。我們為一些流行的構建系統提供了構建文件: msvc/ for Visual Studio, xcode/ for Mac Xcode, make/ for GNU make, codegear/ for Borland C++ Builder.

如果你的構建系統不在這個名單上,在googletest根目錄有autotools的腳本(不推薦使用)和CMakeLists.txtCMake(推薦)。你可以看看make / Makefile來瞭解如何編譯Google Test(基本上你想在頭文件中使用GTEST_ROOT和GTEST_ROOT / include來編譯src / gtest-all.cc路徑,其中GTEST_ROOT是Google測試根目錄)。

  一旦你能夠編譯google test庫,您應該為您的測試程式創建一個項目或構建目標。Make sure you have GTEST_ROOT/include in the header search path so that the compiler can find "gtest/gtest.h" when compiling your test.把google test庫加到你的測試項目中(比如:在VS中在gtest.vcproj上添加依賴)。

二、基本概念

當使用谷歌測試,您首先要寫斷言,斷言是檢查條件是否為真的語句。一個斷言的結果可以是成功,非致命性失敗,或致命的失敗。如果一個致命失敗出現,它會終止當前的函數;否則程式繼續正常運行。

測試使用斷言驗證代碼的行為。如果一個測試崩潰或者有一個失敗的斷言,那麼失敗;否則成功。

一個測試用例包含一個或多個測試。 您應該將測試分組為反映測試代碼結構的測試用例。當測試用例中的多個測試需要共用公共的對象和子程式時,你可以把它們放進一個test fixture class(測試夾具類)。

一個測試程式可以包含多個測試用例。

現在我們將解釋如何編寫測試程式,從單個斷言級別開始,並構建測試和測試用例。

三、斷言

    Google Test斷言是類似於函數調用的巨集。您可以通過對其行為進行斷言來測試類或函數。當斷言失敗時,Google Test會列印斷言的源文件和行號位置以及失敗消息。您還可以提供自定義失敗消息,該消息將附加到Google測試的信息中。

斷言是成對的,測試同一件事,但對當前函數有不同的影響。 ASSERT_ *版本在失敗時會生成致命錯誤,並中止當前函數。 EXPECT_ *版本生成非致命性故障,不會中止當前函數。通常優先使用EXPECT_ *,因為它們允許在測試中報告多個故障。但是,如果失敗時函數繼續運行沒有意義,則應使用ASSERT_ *。

因為失敗的ASSERT_ *立即從當前函數返回,可能跳過其後的清理代碼,它可能導致資源泄漏。根據泄漏的性質,它可能值得修複也可能不值得修複--所以把這個記在心裡,如果你有一個堆檢測錯誤需要註意是什麼導致的。

要提供自定義失敗消息,只需使用<<運算符或一系列此類運算符將其流式傳輸到巨集中即可。一個例子:

ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

for (int i = 0; i < x.size(); ++i) {
  EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}

  示例:

EXPECT_EQ(0, strcmp(s.c_string(), kHelloString2)) << "s.c_string:" << s.c_string() << " kHelloString:" << +kHelloString ;  

 

   任何可以流式傳輸到ostream的東西都可以流式傳輸到斷言巨集,特別是C字元串和字元串對象。 如果一個寬字元串(Windows上的wchar_t *,TCHAR *在UNICODE模式下,或者std :: wstring)被流化到一個斷言,當列印時它將被轉換為UTF-8。

四、基本斷言

這些斷言做基本的真/假條件測試。

Fatal assertionNonfatal assertionVerifies
ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition is true
ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition is false

記住,當它們失敗時,ASSERT_ *產生致命失敗並從當前函數返回,而EXPECT_ *產生非致命失敗,允許函數繼續運行。 在任一情況下,斷言失敗意味著其包含的測試失敗。

五、二進位比較

本節介紹比較兩個值的斷言。

Fatal assertionNonfatal assertionVerifies
ASSERT_EQ(val1,val2); EXPECT_EQ(val1,val2); val1 == val2
ASSERT_NE(val1,val2); EXPECT_NE(val1,val2); val1 != val2
ASSERT_LT(val1,val2); EXPECT_LT(val1,val2); val1 < val2
ASSERT_LE(val1,val2); EXPECT_LE(val1,val2); val1 <= val2
ASSERT_GT(val1,val2); EXPECT_GT(val1,val2); val1 > val2
ASSERT_GE(val1,val2); EXPECT_GE(val1,val2); val1 >= val2

在發生故障時,Google測試同時列印val1和val2。

值參數通過斷言的比較運算符必須可以比較,否則會出現編譯錯誤。我們曾經要求參數支持<<運算符,用於流傳輸到ostream,但從v1.6.0它不再需要(如果支持<<,則會在斷言失敗時調用它來列印參數;否則Google Test將嘗試以最佳方式列印它們。有關更多詳細信息和如何自定義參數的列印,請參閱此Google Mock recipe.。

這些斷言可以使用用戶定義的類型,但是只有當你定義了相應的比較運算符(例如==,<,etc)。如果定義了相應的操作符,則更喜歡使用ASSERT _ *()巨集,因為它們不僅會列印比較結果,而且還會列印出兩個操作數。

參數總是只計算一次。因此,參數有副作用沒關係。然而,與任何普通的C / C ++函數一樣,參數的求值順序是未定義的(即編譯器可以自由選擇任何順序),你的代碼不應該依賴於任何特定的參數求值順序。

ASSERT_EQ()指針的指針相等。如果在兩個C字元串上使用,它會測試它們是否在同一個記憶體位置,而不是它們具有相同的值。因此,如果你想比較C字元串(例如const char *)的值,使用ASSERT_STREQ(),稍後將會描述。特別地,要斷言C字元串為NULL,請使用ASSERT_STREQ(NULL,c_string)。但是,要比較兩個字元串對象,應該使用ASSERT_EQ。

本節中的巨集適用於窄和寬字元串對象(string和wstring)。

歷史記錄:2016年2月之前* _EQ有一個約定,稱為ASSERT_EQ(expected,actual),所以很多現有的代碼使用這個順序。 現在* _EQ以相同的方式處理這兩個參數。

六、字元串比較

該組中的斷言比較兩個C字元串的值。 如果要比較兩個字元串對象,請改用EXPECT_EQ,EXPECT_NE和etc。

Fatal assertionNonfatal assertionVerifies
ASSERT_STREQ(str1,str2); EXPECT_STREQ(str1,_str_2); the two C strings have the same content
ASSERT_STRNE(str1,str2); EXPECT_STRNE(str1,str2); the two C strings have different content
ASSERT_STRCASEEQ(str1,str2); EXPECT_STRCASEEQ(str1,str2); the two C strings have the same content, ignoring case(忽略大小寫)
ASSERT_STRCASENE(str1,str2); EXPECT_STRCASENE(str1,str2); the two C strings have different content, ignoring case

 

註意,斷言名中的“CASE”表示忽略大小寫。

* STREQ *和* STRNE *也接受寬C字元串(wchar_t *)。 如果兩個寬字元串的比較失敗,它們的值將列印為UTF-8窄字元串。

NULL指針和空字元串被認為是不同的。

可用性:Linux,Windows,Mac。

另請參閱:有關更多字元串比較技巧(例如,子字元串,首碼,尾碼和正則表達式匹配),請參見高級Google測試指南(Advanced Google Test Guide.)。

七、簡單測試

創建測試:

  1.使用TEST()巨集來定義和命名測試函數,這些是不返回值的普通C++函數。
  2.在此函數中,連同要包括的任何有效的C++語句,使用各種Google Test斷言來檢查值。
  3.測試的結果由斷言確定; 如果測試中的任何斷言失敗(致命或非致命),或者如果測試崩潰,則        整個測試失敗。 否則,它成功。

TEST(test_case_name, test_name) {
 ... test body ...
}

TEST()參數從一般到特定。 第一個參數是測試用例的名稱,第二個參數是測試用例中的測試名稱。 這兩個名稱必須是有效的C ++標識符,並且它們不應包含下劃線(_)。 測試的全名由其包含的測試用例及其個人名稱組成。來自不同測試用例的測試可以具有相同的個人名稱。

例如,讓我們使用一個簡單的整數函數:

int Factorial(int n); // Returns the factorial of n;n!

  此函數的測試用例可能如下所示:

// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
  EXPECT_EQ(1, Factorial(0));
}

// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
  EXPECT_EQ(1, Factorial(1));
  EXPECT_EQ(2, Factorial(2));
  EXPECT_EQ(6, Factorial(3));
  EXPECT_EQ(40320, Factorial(8));
} 

Google Test通過測試用例對測試結果進行分組,因此邏輯相關的測試應該在同一測試用例中; 換句話說,它們的TEST()的第一個參數應該是相同的。 在上面的例子中,我們有兩個測試,HandlesZeroInput和HandlesPositiveInput,屬於同一個測試用例FactorialTest。

八、測試夾具(Test Fixtures):對多個測試使用相同的數據配置

如果你發現自己寫了兩個或更多的測試來操作類似的數據,你可以使用測試夾具。它允許您為幾個不同的測試重覆使用相同的對象配置。

要創建夾具,只需:

  1.從:: testing :: Test派生一個類。 使用protected:或public:開始它的主體,因為我們想從子類     訪問fixture成員。
  2.在類中,聲明你打算使用的任何對象。
  3.如果需要,可以編寫預設構造函數或SetUp()函數來為每個測試準備對象。 一個常見的錯誤是     拼寫SetUp()為Setup()與一個小u -- 不要讓這種情況發生在你身上。
  4.如果需要,寫一個析構函數或TearDown()函數來釋放你在SetUp()中分配的任何資源。 要     學習什麼時候應該使用構造函數/析構函數,當你應該使用SetUp()/ TearDown()時,請閱讀這個 FAQ entry.。
  5.如果需要,定義要分享的測試的子程式。

當使用夾具時,使用TEST_F()而不是TEST(),因為它允許您訪問測試夾具中的對象和子程式:

TEST_F(test_case_name, test_name) {
 ... test body ...
}

和TEST()一樣,第一個參數是測試用例名,

但是對於TEST_F()第一個參數必須是測試夾具類的名稱。 你可能猜到了:_F是夾具。

不幸的是,C ++巨集系統不允許我們創建一個可以處理兩種類型的測試的巨集。 使用錯誤的巨集會導致編譯器錯誤。

另外,在TEST_F()中使用它之前,你必須首先定義一個測試夾具類,否則將得到編譯器錯誤“virtual outside class declaration”。

對於使用TEST_F()定義的每個測試,Google Test將:

  1.在運行時創建一個新的測試夾具
  2.立即通過SetUp()初始化,
  3.運行測試
  4.通過調用TearDown()清除
  5.刪除測試夾具。 請註意,同一測試用例中的不同測試具有不同的測試夾具對象,Google測試始     終會刪除測試夾具,然後再創建下一個測試夾具。 Google測試不會為多個測試重覆使用相同的       測試夾具。一個測試對夾具的任何更改不會影響其他測試

例如,讓我們為名為Queue的FIFO隊列類編寫測試,它有以下介面:

template <typename E> // E is the element type.
class Queue {
 public:
  Queue();
  void Enqueue(const E& element);
  E* Dequeue(); // Returns NULL if the queue is empty.
  size_t size() const;
  ...
};

  首先定義一個夾具類。按照慣例,你應該給它名稱FooTest,其中Foo是被測試的類。

class QueueTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    q1_.Enqueue(1);
    q2_.Enqueue(2);
    q2_.Enqueue(3);
  }

  // virtual void TearDown() {}

  Queue<int> q0_;
  Queue<int> q1_;
  Queue<int> q2_;
};

在這種情況下,不需要TearDown(),因為我們不必在每次測試後清理,除了析構函數已經做了什麼。

現在我們將使用TEST_F()和這個夾具編寫測試。

TEST_F(QueueTest, IsEmptyInitially) {
  EXPECT_EQ(0, q0_.size());
}

TEST_F(QueueTest, DequeueWorks) {
  int* n = q0_.Dequeue();
  EXPECT_EQ(NULL, n);

  n = q1_.Dequeue();
  ASSERT_TRUE(n != NULL);
  EXPECT_EQ(1, *n);
  EXPECT_EQ(0, q1_.size());
  delete n;

  n = q2_.Dequeue();
  ASSERT_TRUE(n != NULL);
  EXPECT_EQ(2, *n);
  EXPECT_EQ(1, q2_.size());
  delete n;
}

上面使用ASSERT_ *和EXPECT_ *斷言。 經驗法則( The rule of thumb )是當你希望測試在斷言失敗後繼續顯示更多錯誤時使用EXPECT_ *,或是在失敗後繼續使用ASSERT_ *沒有意義。 例如,Dequeue測試中的第二個斷言是ASSERT_TRUE(n!= NULL),因為我們需要稍後解引用指針n,這將導致n為NULL時的segfault。

當這些測試運行時,會發生以下情況:

  1.Google Test構造了一個QueueTest對象(我們稱之為t1)。 
  2.t1.SetUp()初始化t1。 
  3.第一個測試(IsEmptyInitially)在t1上運行。 
  4.t1.TearDown()在測試完成後清理。 
  5.t1被析構。 
  6.以上步驟在另一個QueueTest對象上重覆,這次運行DequeueWorks測試。

九、調用測試

TEST()和TEST_F()用Google Test隱式註冊他們的測試。 因此,與許多其他C ++測試框架不同,您不必重新列出所有定義的測試以運行它們。

定義測試後,可以使用RUN_ALL_TESTS()運行它們,如果所有測試成功則返回0,否則返回1。 請註意,RUN_ALL_TESTS()運行鏈接單元中的所有測試 - 它們可以來自不同的測試用例,甚至是不同的源文件。

調用時,RUN_ALL_TESTS()巨集:

  1. 保存所有Google測試標記的狀態。
  2. 為第一個測試創建測試夾具對象。
  3. 通過SetUp()初始化它。
  4. 在fixture對象上運行測試。
  5. 通過TearDown()清除夾具。
  6. 刪除夾具。
  7. 恢復所有Google測試標誌的狀態。
  8. 重覆上述步驟進行下一個測試,直到所有測試運行結束。

此外,如果測試夾具的構造函數在步驟2中產生致命故障,則步驟3-5沒有意義,因此它們被跳過。 類似地,如果步驟3產生致命故障,則將跳過步驟4。

重要:您不能忽略RUN_ALL_TESTS()的返回值,否則gcc將給您一個編譯器錯誤。 此設計的基本原理是自動測試服務基於其退出代碼而不是其stdout / stderr輸出來確定測試是否已通過; 因此您的main()函數必須返回RUN_ALL_TESTS()的值

此外,您應該只調用一次RUN_ALL_TESTS()。 多次調用它會與一些高級Google測試功能(例如線程安全死亡測試)衝突,因此不受支持。

 十、寫Main函數

你可以從這個樣板開始:

#include "this/package/foo.h"
#include "gtest/gtest.h"

namespace {
// The fixture for testing class Foo.
class FooTest : public ::testing::Test {
 protected:
  // You can remove any or all of the following functions if its body
  // is empty.

  FooTest() {
    // You can do set-up work for each test here.
  }

  virtual ~FooTest() {
    // You can do clean-up work that doesn't throw exceptions here.
  }

  // If the constructor and destructor are not enough for setting up
  // and cleaning up each test, you can define the following methods:

  virtual void SetUp() {
    // Code here will be called immediately after the constructor (right
    // before each test).
  }

  virtual void TearDown() {
    // Code here will be called immediately after each test (right
    // before the destructor).
  }

  // Objects declared here can be used by all tests in the test case for Foo.
};

// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, MethodBarDoesAbc) {
  const string input_filepath = "this/package/testdata/myinputfile.dat";
  const string output_filepath = "this/package/testdata/myoutputfile.dat";
  Foo f;
  EXPECT_EQ(0, f.Bar(input_filepath, output_filepath));
}

// Tests that Foo does Xyz.
TEST_F(FooTest, DoesXyz) {
  // Exercises the Xyz feature of Foo.
}

}  // namespace

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

:: testing :: InitGoogleTest()函數解析Google測試標誌的命令行,並刪除所有已識別的標誌。 這允許用戶通過各種標誌控制測試程式的行為,我們將在AdvancedGuide中介紹。 在調用RUN_ALL_TESTS()之前必須調用此函數,否則標誌將無法正確初始化。

在Windows上,InitGoogleTest()也適用於寬字元串,因此它也可以在以UNICODE模式編譯的程式中使用。

但也許你認為編寫所有這些main()函數是太多的工作? 我們完全同意你的看法,這就是為什麼Google Test提供了main()的基本實現。 如果它適合你的需要,然後只是鏈接你的測試與gtest_main庫。

十 一、Visual C ++用戶的重要說明

如果你把你的測試放入一個庫,你的main()函數在不同的庫或在你的 .exe文件中,這些測試將不會運行。 原因是Visual C ++中的一個錯誤。 當您定義測試時,Google測試會創建一些靜態對象來註冊它們。 這些對象沒有從其他地方引用,但它們的構造函數仍然應該運行。 當Visual C ++鏈接器發現庫中沒有從其他地方引用時,它會拋出該庫。 您必須通過主程式中的測試來引用您的庫,以防止鏈接器丟棄它。 這裡是如何做到的。 在你的庫代碼中聲明一個函數:

。。。。。省略啦,如果用VC再來看。

十二、從這裡到哪裡去

恭喜! 您已學習了Google測試基礎知識。 您可以開始編寫和運行Google Test測試,閱讀一些示例,或繼續閱讀AdvancedGuide,其中介紹了更多有用的Google測試功能。

 

十三、已知的限制

Google測試旨線上程安全。 在pthreads庫可用的系統上,實現是線程安全的。 目前,在其他系統(例如Windows)上同時使用兩個線程的Google Test斷言是不安全的。 在大多數測試中,這不是一個問題,因為通常斷言是在主線程中完成的。 如果你想幫助,你可以志願為您的平臺在gtest-port.h中實現必要的同步原語。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這是繼上次svn 客戶端與伺服器安裝後的如何在Eclipse 環境下線上安裝 SVN插件,我的Eclipse版本是4.50 SVN的線上安裝 下麵為大家提供SVN 的線上安裝教程。下麵是安裝的 詳細過程: 1.打開Eclipse的help的Install New Software 其中http:// ...
  • ...
  • 1、簡介 EventBus是一個Android端優化的publish/subscribe消息匯流排,簡化了應用程式內各組件間、組件與後臺線程間的通信。比如請求網路,等網路返回時通過Handler或Broadcast通知UI,兩個Fragment之間需要通過Listener通信,這些需求都可以通過Eve ...
  • 1、簡介 ButterKnife是註解中相對簡單易懂的很不錯的開源框架 1.強大的View綁定和Click事件處理功能,簡化代碼,提升開發效率 2.方便的處理Adapter里的ViewHolder綁定問題 3.運行時不會影響APP效率,使用配置方便 4.代碼清晰,可讀性強 2、下載地址 https: ...
  • 有不足之處,請大家指出 一、 基礎知識 1、SDK的最新下載 搜索oracle,進入網站,à Downloads –> JavaSEI à 選第一個下載(其實java 8u111和8u112的區別就是在8u111的基礎上優化了一下,升了下級,實際使用沒什麼區別的) 其次註意一下選32位還是64位,是 ...
  • 無限級分類是一種設計技巧,在開發中經常使用,例如:網站目錄、部門結構、文章分類。筆者覺得它在對於設計表的層級結構上面發揮很大的作用,比如大家在一些平臺上面, 填寫邀請人,它就是一種上下級的關係,上級會有多個下級,下級又會有自己的分支,大多數都是利用遞歸的思想去實現。話不多說,首先來溫故一下遞歸的實現 ...
  • 如果你還沒有搭建gtest框架,可以參考我之前的博客:http://www.cnblogs.com/jycboy/p/6001153.html。。 1.The first sample: sample1 你把github上的項目導來之後,github地址:https://github.com/goo ...
  • RPC即遠程過程調用,它的實現方式有很多,比如webservice等。框架調多了,煩了,沒激情了,我們就該問自己,這些框架的作用到底是什麼,來找回當初的激情。 一般來說,我們寫的系統就是一個單機系統,一個web伺服器一個資料庫服務,但是當這單台伺服器的處理能力受硬體成本的限制,是不能無限的提升處理性 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...