日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

针对故障场景的血液,汗液和书写自动集成测试

發布時間:2023/12/3 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 针对故障场景的血液,汗液和书写自动集成测试 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

去年冬天,我為仍在工作的客戶編寫并發布了一項服務。 總體而言,該服務滿足了業務需求和性能要求,但是使用該服務的一個團隊告訴我,他們定期遇到一個問題,該問題是該服務將返回500個錯誤,并且在重新啟動該服務之前不會恢復正常。 我問這是什么時候發生的, 戴上了偵探的帽子。

在此博客中,我將介紹診斷錯誤并確定正確的集成測試解決方案以正確方式進行修復的過程。 為此,我必須創建一個測試,以準確再現服務在PROD中遇到的情況。 我必須創建一個修復程序,使測試從失敗到通過。 最后,我努力提高對所有未來發行版代碼正確性的信心,這只有通過自動測試才能實現。

診斷錯誤

在500個錯誤開始發生時,我會仔細閱讀服務的日志文件。 他們很快發現了一個非常嚴重的問題:在星期六的午夜之前,我的服務將開始引發錯誤。 最初,所有SQLException都發生了各種各樣的錯誤,但最終根本原因是相同的:

org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLRecoverableException: IO Error: The Network Adapter could not establish the connectionat org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)

此過程持續了幾個小時,直到次日凌晨重新啟動服務,服務恢復正常為止。

與檢查 洞穴巨魔 DBA,我發現要連接的數據庫已關閉以進行維護。 確切的細節使我無所適從,但我認為這是數據庫關閉的大約30分鐘的窗口。 因此,很明顯,一旦數據庫從中斷中恢復,我的服務就無法重新連接到數據庫。

修復此錯誤(和我過去經常去過的錯誤)的最直接方法是使用Google“從數據庫中斷中恢復”,這很可能導致我遇到一個Stack Overflow線程,該線程可以回答我的問題。 然后,我將在提供的答案中“復制并粘貼”并推送要測試的代碼。

如果生產受到錯誤的嚴重影響,則在短期內可能需要使用此方法。 就是說,應該在不久的將來留出時間來用自動測試來覆蓋更改。

因此,通常情況下,“正確的方式”做事通常意味著大量的字體加載時間投資,這句話在這里肯定是正確的。

但是,投資回報是花費在修復錯誤上的時間減少了,對代碼正確性的信心增加了,此外,測試可以作為文檔在給定場景下的行為的重要形式。

盡管這個特定的測試用例有些深奧,但在設計和編寫測試(無論是單元測試還是集成測試)時要牢記這一重要因素:給測試起好名字,確保測試代碼可讀性,等等。

解決方案1:模擬一切

我為該問題編寫測試的第一個步驟是嘗試“模擬一切”。 盡管Mockito和其他模擬框架非常強大,并且變得越來越容易使用,但在考慮了此解決方案之后,我很快得出結論,就是我永遠不會有信心,除了模擬之外,我不會進行任何測試已經寫了。

獲得“綠色”結果并不會增加我對代碼正確性的信心,而這首先是編寫自動化測試的全部要點! 轉到另一種方法。

解決方案2:使用內存數據庫

我編寫測試的下一個嘗試是使用內存數據庫。 我是H2的忠實擁護者,過去我廣泛使用H2,希望它可以再次滿足我的需求。 我在這里的時間可能比我應該花費的時間多。

雖然最終這種方法沒有成功,但花費的時間并沒有完全浪費,我確實學到了更多有關H2的知識。 以“正確的方式”做事的好處之一(盡管此刻通常很痛苦)是您可以學到很多東西。 所獲得的知識在當時可能沒有用,但以后可能會有價值。

使用內存數據庫的優勢

就像我說的那樣,我在這里的時間可能比我應該花的時間更多,但是我確實有希望這種解決方案起作用的原因。 H2和其他內存數據庫具有兩個非常理想的特征:

  • 速度: H2的啟動和停止相當快,不到一秒。 因此,盡管比使用模擬慢一些,但我的測試仍會很快。
  • 可移植性: H2可以完全從導入的jar運行,因此其他開發人員可以僅提取我的代碼并運行所有測試,而無需執行任何其他步驟。

另外,我最終的解決方案有兩個非常重要的缺點,下面將作為解決方案的一部分進行介紹。

編寫測試

有點有意義,但是到目前為止,我還沒有編寫任何一行生產代碼。 TDD的主要原則是先編寫測試,然后編寫生產代碼。 這種方法論以及確保高水平的測試覆蓋率還鼓勵開發人員僅進行必要的更改。 這回到了提高對代碼正確性的信心這一目標。

以下是我用來測試PROD問題的初始測試用例:

@RunWith(SpringRunner.class) @SpringBootTest(classes = DataSourceConfig.class, properties = {"datasource.driver=org.h2.Driver", "datasource.url=jdbc:h2:mem:;MODE=ORACLE", "datasource.user=test", "datasource.password=test" }) public class ITDatabaseFailureAndRecovery {@Autowiredprivate DataSource dataSource;@Testpublic void test() throws SQLException {Connection conn = DataSourceUtils.getConnection(dataSource);conn.createStatement().executeQuery("SELECT 1 FROM dual");ResultSet rs = conn.createStatement().executeQuery("SELECT 1 FROM dual");assertTrue(rs.next());assertEquals(1, rs.getLong(1));conn.createStatement().execute("SHUTDOWN");DataSourceUtils.releaseConnection(conn, dataSource);conn = DataSourceUtils.getConnection(dataSource);rs = conn.createStatement().executeQuery("SELECT 1 FROM dual");assertTrue(rs.next());assertEquals(1, rs.getLong(1));} }

最初,我覺得使用此解決方案的方向正確。 有一個問題是如何啟動H2服務器備份(一次有一個問題!),但是當我運行測試時,它失敗了,并給出了與我的服務在PROD中所經歷的類似的錯誤:

org.h2.jdbc.JdbcSQLException: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-192]

但是,如果我修改測試用例并僅嘗試第二次連接數據庫:

conn = DataSourceUtils.getConnection(dataSource);

異常消失了,我的測試通過了,而無需更改生產代碼。 這里不對勁…

為什么此解決方案不起作用

因此,使用H2將不起作用。 實際上,我花了很多時間嘗試使H2正常工作,而不是上面建議的時間。 包括故障排除嘗試; 連接到基于文件的H2服務器實例,而不只是一個內存中的遠程H2服務器; 我什至偶然發現了H2 Server類 , 該類本來可以解決早先的服務器關閉/啟動問題。

這些嘗試顯然都沒有效果。 H2的基本問題(至少對于此測試用例而言)是,嘗試連接到數據庫(如果當前未運行)將導致該數據庫啟動。 正如我的初始測試用例所示,這有點延遲,但是顯然這構成了一個基本問題。 在PROD中,當我的服務嘗試連接到數據庫時,它不會導致數據庫啟動(無論我嘗試連接多少次)。 我的服務日志肯定可以證明這一事實。 接下來是另一種方法。

解決方案3:連接到本地數據庫

模擬一切都行不通。 使用內存數據庫也不會成功。 看來,我能夠正確重現我的服務在PROD中遇到的方案的唯一方法是連接到更正式的數據庫實現。 關閉共享開發數據庫是不可能的,因此該數據庫實現需要在本地運行。

該解決方案的問題

因此,在此之前的所有內容都應該很好地表明我確實希望避免走這條路。 我的沉默有一些很好的理由:

  • 降低的可移植性:如果其他開發人員想要運行此測試,則需要在本地計算機上下載并安裝數據庫。 她還需要確保她的配置詳細信息符合測試的期望。 這是一項耗時的任務,并且至少會導致一定數量的“帶外”知識。
  • 速度較慢:總體而言,我的測試仍然不太慢,但是啟動,關閉和重新啟動(即使是針對本地數據庫)也需要花費幾秒鐘的時間。 雖然幾秒鐘聽起來不算多,但可以通過足夠的測試來累加時間。 這是一個主要的問題,因為允許集成測試花費更長的時間(以后要花更多的時間),但是集成測試越快,運行它們的頻率就越高。
  • 組織爭執:要在構建服務器上運行此測試,意味著我現在需要與已經負擔過重的DevOps團隊合作,在構建框中設置數據庫。 即使操作團隊沒有負擔過重,我也想盡可能避免這種情況,因為這只是又一步。
  • 許可:在我的代碼示例中,我使用MySQL作為測試數據庫實現。 但是,對于我的客戶,我正在連接到Oracle數據庫。 Oracle確實免費提供了Oracle Express Edition(XE),但確實有規定。 這些規定之一是不能同時運行兩個Oracle XE實例。 除了Oracle XE的特殊情況外,在連接到特定產品時,許可可能成為一個問題,這一點要牢記。

…最后

最初,這篇文章要長很多,這也給所有 鮮血,汗水和眼淚 到現在為止的工作。 最終,這些信息對讀者而言并不是特別有用,即使這是作者寫信的方式。 因此,事不宜遲,一個測試可以準確地重現我的服務在PROD中遇到的情況:

@Test public void testServiceRecoveryFromDatabaseOutage() throws SQLException, InterruptedException, IOException {Connection conn = null;conn = DataSourceUtils.getConnection(datasource);assertTrue(conn.createStatement().execute("SELECT 1"));DataSourceUtils.releaseConnection(conn, datasource);LOGGER.debug("STOPPING DB");Runtime.getRuntime().exec("/usr/local/mysql/support-files/mysql.server stop").waitFor();LOGGER.debug("DB STOPPED");try {conn = DataSourceUtils.getConnection(datasource);conn.createStatement().execute("SELECT 1");fail("Database is down at this point, call should fail");} catch (Exception e) {LOGGER.debug("EXPECTED CONNECTION FAILURE");}LOGGER.debug("STARTING DB");Runtime.getRuntime().exec("/usr/local/mysql/support-files/mysql.server start").waitFor();LOGGER.debug("DB STARTED");conn = DataSourceUtils.getConnection(datasource);assertTrue(conn.createStatement().execute("SELECT 1"));DataSourceUtils.releaseConnection(conn, datasource); }

完整代碼在這里: https : //github.com/wkorando/integration-test-example/blob/master/src/test/java/com/integration/test/example/ITDatabaseFailureAndRecovery.java

修復

所以我有我的測試用例。 現在是時候編寫生產代碼以使我的測試顯示為綠色。 最終,我從一個朋友那里得到了答案,但是可能會在使用足夠的谷歌搜索功能時偶然發現了這個答案。

最初,我在服務配置中設置的數據源實際上看起來像這樣:

@Bean public DataSource dataSource() {org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();dataSource.setDriverClassName(env.getRequiredProperty("datasource.driver"));dataSource.setUrl(env.getRequiredProperty("datasource.url"));dataSource.setUsername(env.getRequiredProperty("datasource.user"));dataSource.setPassword(env.getRequiredProperty("datasource.password"));return dataSource; }

我的服務遇到的潛在問題是,當來自DataSource的連接池的連接未能連接到數據庫時,它變得“不好”。 然后,下一個問題是我的DataSource實現不會從連接池中刪除這些“不良”連接。 它只是不斷嘗試使用它們。

幸運的是,此修復非常簡單。 當DataSource從連接池中檢索連接時,我需要指示DataSource測試連接。 如果此測試失敗,則連接將從池中刪除,并嘗試建立新的連接。 我還需要為DataSource提供一個查詢,它可以用來測試連接。

最后(并非絕對必要,但對測試很有用),默認情況下,我的DataSource實現僅每30秒測試一次連接。 但是,我的測試可以在不到30秒的時間內運行。 最終,這段時間的長度并沒有真正意義,因此我添加了一個由屬性文件提供的驗證間隔。

這是我更新后的DataSource外觀:

@Bean public DataSource dataSource() {org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();dataSource.setDriverClassName(env.getRequiredProperty("datasource.driver"));dataSource.setUrl(env.getRequiredProperty("datasource.url"));dataSource.setUsername(env.getRequiredProperty("datasource.user"));dataSource.setPassword(env.getRequiredProperty("datasource.password"));dataSource.setValidationQuery("SELECT 1");dataSource.setTestOnBorrow(true);dataSource.setValidationInterval(env.getRequiredProperty("datasource.validation.interval"));return dataSource; }

關于編寫集成測試的最后一點說明。 最初,我創建了一個測試配置文件,該文件用于配置要在測試中使用的DataSource 。 但是,這是不正確的。

問題是,如果有人要從生產配置文件中刪除我的修訂,但將其保留在測試配置文件中,則我的測試仍會通過,但是我的實際生產代碼將再次受到我這段時間花的問題的侵害。定影! 這是一個容易想象的錯誤。 因此,在編寫集成測試時,請確保使用實際的生產配置文件。

自動化測試

因此,即將結束。 我有一個測試用例,可以準確地重現我在PROD中遇到的情況。 我有一個修復程序,然后使我的測試從失敗到通過。 但是,所有這些工作的重點不僅是讓我確信我的修訂適用于下一個版本,而且還適用于所有將來的版本。

Maven用戶:希望您已經熟悉surefire插件 。 或者,至少希望您的DevOps團隊已經設置了父pom,以便在構建服務器上構建項目時,每次提交都會花費您花時間編寫的所有單元測試。

但是,本文不是關于編寫單元測試的,而是關于編寫集成測試的 。 集成測試套件的運行時間(有時數小時)通常比單元測試套件的時間(不超過5-10分鐘)長得多。 集成測試通常也更容易波動。 雖然我在本文中編寫的集成測試應該是穩定的-如果它破裂了,應該引起關注-在連接到開發數據庫時,您不能總是100%確信數據庫將可用或測試數據將是正確的甚至是存在的。 因此,失敗的集成測試并不一定意味著代碼不正確。

幸運的是,Maven背后的人們已經解決了這個問題,那就是帶有故障安全插件 。 默認情況下,surefire插件將查找Test之前或之后固定的類,而failsafe插件將查找IT (集成測試)之前或之后固定的類。 像所有Maven插件一樣,您可以配置插件應執行的目標。 這使您可以靈活地使每次代碼提交都運行單元測試,而集成測試僅在夜間構建時運行。 這也可以防止需要部署修補程序但不存在集成測試所依賴的資源的情況。

最后的想法

編寫集成測試既耗時又困難。 它需要廣泛考慮您的服務將如何與其他資源交互。 當您專門測試故障場景時,此過程甚至更加困難且耗時,這通常需要對測試所連接的資源進行更深入的控制,并借鑒過去的經驗和知識。

盡管花費了大量的時間和精力,但這項投資將隨著時間的推移多次收回投資。 只有通過自動測試才能提高對代碼正確性的信心,這對縮短開發反饋周期至關重要。

我在本文中使用的代碼可以在這里找到: https : //github.com/wkorando/integration-test-example 。

翻譯自: https://www.javacodegeeks.com/2016/10/blood-sweat-writing-automated-integration-tests-failure-scenarios.html

總結

以上是生活随笔為你收集整理的针对故障场景的血液,汗液和书写自动集成测试的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。