Tubi 前端測試:遷移 Enzyme 到 React Testing Library

前端技術發展迅速,即便不說是日新月異,每年也都推出新框架和新技術。Tubi 的產品前端代碼倉庫始建于 2015 年,至今 8 年有余。可喜的是,多年來緊隨 React 社區的發展,Tubi 絕大多數的基礎框架選型都遵循了社區流行的最佳實踐。核心框架和依賴的版本基本都已經或有計劃更新到最新的穩定版本。

能做到這一點,主要得益于 Tubi 小而美的前端團隊有著強烈的技術自驅力;此外,團隊管理層重視工程師文化和技術基礎設施建設,愿意給予團隊不少于 20% 的整塊時間進行功能開發外的必要技術優化和升級。

本文將介紹的 Enzyme 到 React Testing Library(RTL)的遷移發生在 2022 年底,是 Tubi 前端至關重要且有相當工作量的代碼遷移項目之一。

遷移動機

在 Tubi ,即便是社區推薦的技術選型,我們也要先對其必要性和價值做出評估,待達成共識后才會采取進一步的行動。回到 Enzyme 到 RTL 的遷移,主要理由有四個:

第一,Airbnb 官方已經不再活躍維護 Enzyme,且沒有計劃支持最新的 React 18。Tubi 決定升級到最新的 React 18 ,就必須找到并遷移所有 UI 測試到 Enzyme 的替代方案。

第二,RTL 關注于集成測試的設計理念使得團隊可以更輕易、高效地寫出易于維護的測試代碼。

第三,RTL 鼓勵從用戶實際使用角度寫測試用例,因此其 API 設計理念無形中就引入很多 UI 測試的最佳實踐,例如對 Web Accessibility 的關注和強調。

第四,RTL 成熟活躍的社區及輕量的實現機制保證了該測試框架可預期的長久生命力。

關于第二點,相對于大家所熟知的測試金字塔模型,RTL 的作者 Kent C. Dods 提出了與之相對應的測試獎杯模型(Test Trophy),如下圖所示。Kent 認為,應該把主要精力放到寫 UI 集成測試(Integration Test)上,這樣模擬 API 調用和返回結果去整合應用,既可以規避因調用異步 API 導致過慢的測試運行速度,又能從用戶實際使用角度全方位測試應用的功能,做到事半功倍。

test trophy.jpg

圖片來源:testingjavascript.com/

舉例說明,當我們測試頁面渲染時,不僅測試了預期被渲染的 UI 元素,同時自然而然地覆蓋了該頁面數據加載、UI 元素展示和潛在的用戶交互及權限控制。換句話說,我們通過 UI 測試用例,就可以順帶自然而然地觸及更底層的有關 Redux 狀態管理、事件派發和數據組織的功能和邏輯,從而輕松覆蓋原本需要單元測試去檢測的代碼功能和分支條件。因此,UI 集成測試兼顧了測試覆蓋度和測試運行速度,是更有效、值得推薦的書寫前端測試代碼的方式。

第三點中提到的 RTL 先進測試理念在下文 Counter 測試的示例中展示無遺。Enzyme 測試一般會與組件的具體實現細節相綁定。

在示例中,Enzyme 通過 button 對應的 increment class name 定位到自增按鈕,并通過檢查保存在組件內部 state 中 count 的變化來確定自增計數功能被正確執行。而 RTL 通過 getByLabelTextgetByRole?這樣的語義化 API 獲取對應遵循 Web Accessibility 規范的元素,進而通過檢測用戶可見界面的改變而驗證計數器自增功能被如預期執行。因此,RTL 測試有兩個顯而易見的好處:

1. 與 UI 實現細節的解耦讓測試代碼更加健壯,今后實現細節的變更并不會導致測試代碼失效;

2. Web Accessibility 導向的 API 設計鼓勵開發者在實現 UI 組件時遵循 Web Accessibility 規范。

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { shallow } from 'enzyme';
import React from 'react';import Counter from './Counter';describe('Enzyme tests', () => {it('should increment by 1', () => {const wrapper = shallow(<Counter />);const instance = wrapper.instance();expect(instance.state.count).toBe(0);wrapper.find('button.increment').simulate('click');expect(instance.state.count).toBe(1);});
});describe('RTL tests', () => {it('should increment by 1', () => {render(<Counter />);const countLabel = screen.getByLabelText('count');expect(countLabel).toHaveTextContent(0);userEvent.click(screen.getByRole('button', { name: 'Increment' }));expect(countLabel).toHaveTextContent(1);});
});作者:隔壁正在裝修真羨慕
鏈接:https://juejin.cn/post/7260024054066085946
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

此外,RTL 和 Enzyme 的 npm 下載次數統計也清晰反映出當前 RTL 已經取代 Enzyme,成為 React 前端項目測試框架的不二選擇。因此,我們將 Enzyme 到 RTL 的遷移工作提上了正式日程。

RTL vs Enzyme.jpg

遷移規劃

了解遷移項目規模和整體概況對于我們制定切實可行的遷移規劃和策略至關重要。Tubi 產品前端代碼倉庫有著高達 92% 的代碼測試覆蓋率。依照遷移項目啟動前的統計,我們依賴 Enzyme 的 UI 測試文件總共有 440+ 個(涉及的總代碼行數應該超過 10 萬行)。如果遷移不能很快完成,隨著新 Enzyme 測試的創建,這個工作量還會持續增加。

因此,我們新功能和新組件的開發需要盡早強制使用 RTL 去測試;同時,對于存量 Enzyme 測試,我們需要找到一個漸進的方案實現逐步遷移。一個分而治之的遷移路徑由此浮現,如下圖所示。

mindmap.jpg

驗證可行性

為了進一步驗證 RTL 是否可以匹配我們對新功能新組建的 UI 測試需求,我們決定從實戰出發,將 RTL 應用到我們當時正在開發的世界杯活動頁面上。選用這個頁面做驗證,是本著先難后易的原則。因為這個著陸頁足夠復雜,涉及響應式 UI 、deep links 調用、API mocks 、用戶交互和頁面跳轉等眾多復雜場景的測試。我們認為,如果 RTL 可以很好地滿足在復雜場景下的測試,那么相對簡單的 UI 測試就更不成問題了。

最終的結果令人滿意,RTL 可以完美適配上述測試場景。相對于 Enzyme 測試,RTL 測試的實現更加簡潔高效,且測試運行時間并沒有顯著改變。同時,我們還封裝了適配 Redux、react-intl 等第三方庫初始化的自定義渲染(render)方法,并沿用?Nock?在 API 層面去模擬 API 的響應結果。這一切,都讓我們得以在盡量維持技術選型和依賴不變的基礎上,做到相對嚴格地遵循 RTL 官方推薦實踐和理念。這些成功而積極的正向反饋堅定了團隊向 RTL 測試遷移的信心。

引入 Lint 和進度統計腳本

延續分而治之的策略,在大規模遷移已有測試前,我們實現了一個自定義的 ESlint 規則并將其添加到 Github CI 中,以確保停止添加新的 Enzyme 測試這一共識被貫徹執行。下圖中,Lint 規則的?startDate?被設為 2022-12-16,這意味著在該日期后引入的新 Enzyme 測試會引發 Lint 報錯。

lint.jpg

同時,為了方便掌握遷移進度,并快速定位尚未遷移的代碼,我們在項目初期就實現了名為?rtl-migrate 的 npm 命令以輔助遷移。如下圖所示,運行 yarn run rtl-migrate?可以獲知 Enzyme 測試的整體遷移進度和選定文件夾的遷移概要。運行 yarn run rtl-migrate -p src/ott --type=enzyme?將打印出 src/ott?目錄下所有尚未遷移的測試文件。此外,該命令還實現了找出測試文件核心貢獻者和根據文件大小過濾排序的功能,這些都為我們分配遷移任務給最恰當的開發者提供了數據參考。文章長度所限,這里不一一展示。

通過 Codemods 自動遷移

根據最初的分析,我們有不少于 440 個測試文件需要遷移。即便按平均每個文件僅 250 行來做最樂觀的估算,整個遷移涉及的測試代碼改寫量將不少于 11 萬行代碼。這無疑是一個巨大且可能曠日持久的工程。為了盡可能縮短工期,我們決定基于 jscodeshift 構建自己的 Codemods 腳本,以便盡可能自動遷移典型的 Enzyme 代碼模式到 RTL 測試,從而節省人工遷移的成本。

選擇自己構建 Codemods 腳本,是因為在開源社區中我們并沒有找到符合要求的現成 Enzyme 遷移腳本。同時考慮到我們項目對 Enzyme 的獨有封裝和擴展,如果希望盡可能靈活精準地將更多 Enzyme 測試自動轉化為 RTL 測試,構建可以完全掌控的 Codemods 腳本勢在必行。

當然,我們并不期望 Codemods 可以遷移所有測試用例。務實的期望是 Codemods 能以最小代價優先覆蓋代碼倉庫中最常見的測試用例和代碼模式。技術選型和決策同樣需要衡量投入產出比和最終收益。平衡和取舍往往是技術決策的關鍵詞。尤其對于測試遷移這類一次性的工作而言,實現一個面面俱到的 Codemods 腳本并不是我們的初衷。重點是,我們須確保通過 Codemods 自動遷移帶來的時間節省收益大于開發 Codemods 付出的精力消耗。

因此,我們優先找出了 Enzyme 測試中最常見的幾種代碼模式及其變形,并實現了對應的 Codemods 使其可以被自動轉換。這一過程切合我們常說的二八原則,即通過 20% 的成本完成了 80% Codemods 的預定目標,剩余 20% 的功能也許我們可以繼續投入 80% 的時間去打磨。考慮到遷移工作一次性投入的特性,對非常見的測試模式我們并沒有強求在 Codemods 層級做自動遷移的支持。

回到具體實現,本質上 Codemods 先將特定代碼片段轉化為抽象語法樹,進而識別并修改特定語法樹的結構后再重新生成新的代碼片段。因此,構建諸如遷移 Enzyme 測試這樣功能復雜的 Codemods 時,對 Codemods 功能的拆解和組織尤其重要;否則,復雜性的不斷疊加終將導致 Codemods 難以維護。實現之初,我們便仔細設計了?rtl-codemod?代碼組織關系和 transformer 間的通信存儲機制。具體說明如下圖。

rtl-codemod.jpg

這里的關鍵詞是解耦,可以將各 transformer 想象為獨立、目的明確的插件或中間件,它們可以互不干擾地獨立運行;最終 Codemods 對代碼的修改是這些插件按順序執行的結果。我們設計?rtl-codemod?時,進一步引入了 motions 這個 micro-transformer 的概念,從而構建了兩層的插件體系結構,做到了 tranformer 功能的進一步拆解和靈活組裝。

作為 Codemods 執行入口的核心,transform 只重點負責配置各 transformers 的執行順序。其實現大體精簡如下:

const transform: Transform = (file, api, options) => {const j = api.jscodeshift;const { source } = file;const ast = j(source);// NOTE: The order of motions is important. Some motions need to be// applied before others.applyMotions(j, ast, [...renderMotions,...snapshotMotions,...findTextMotions,...inTheDocumentMotions,...userEventMotions,// More transform-related motions...]);return toSource(ast, options.toSourceOptions);
};

此外,作為腳本程序,某 transformer 運行時結果和中間計算產出可以通過持久化存儲被其他 tranformers 從特定存儲中讀取。換言之,存儲也可以被理解為 transformer 實現通信和數據共享的媒介。

更多細節可以參考我們開源出來的精簡示例實現:github.com/nickqi-tubi…

妥協和適配

做技術決策時,經常會不得不選用一些退而求其次的妥協方案,但這并不完全是一件壞事。相反,為了更好地適配現有代碼和模式而主動做出的妥協,往往是一種務實而精明的抉擇。

以 Enzyme 遷移為例。

我們在 Enzyme 測試中大量使用了?wrapper.instance() 這種獨有 API 去檢測組件的期望實例值。這是 Enzyme 測試中常見且官方推薦的模式之一。與之相反,RTL 明確反對針對實現細節設計任何測試用例,這也是 RTL 未曾暴露獲取組件實例或內部狀態的 API 的原因。

RTL?官方建議

You want your tests to avoid including implementation details so refactors of your components (changes to implementation but not functionality) don't break your tests and slow you and your team down.

RTL 的設計理念和建議無疑是有道理的。但回到測試遷移這個任務上,考慮到我們已經有至少十萬行代碼基于 Enzyme 實踐,去除對?wrapper.instance() 的依賴意味著我們需要徹底重寫大量測試,這無疑極大加劇了測試遷移的實現成本而難以推進。務實的考慮是,我們需要為 RTL 提供一種橋接,使 RTL 測試依然可以適配現存針對組件實例和內部狀態的測試。

基于上述原因,我們實現了如下所示的?renderWithInstance?工具方法:

function renderWithInstance(passedComponentOptions,renderOptions
) {let incorrectlyUseInstancePattern;let WithExtendedClass;const { extendingClass } = passedComponentOptions;try {if (extendingClass.prototype.isReactComponent) {WithExtendedClass = class WrapperInstance extends extendingClass {constructor(props) {super(props);incorrectlyUseInstancePattern = this;}};} else {WithExtendedClass = class WrapperInstance {constructor(_props: any) {incorrectlyUseInstancePattern = this;}};Object.setPrototypeOf(WithExtendedClass, extendingClass);}} catch (e) {throw new Error(`Problem extending passed 'extendingClass'.\n${e.stack}`);}const renderResult = render(<WithExtendedClass {...passedComponentOptions.props} />,{wrapper: getWrapper(renderOptions),...renderOptions,});return {...renderResult,incorrectlyUseInstancePattern,};
}作者:隔壁正在裝修真羨慕
鏈接:https://juejin.cn/post/7260024054066085946
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

通過在遷移后的 RTL 測試中調用 renderWithInstance,我們使自定義的 RTL 渲染方法依然可以暴露組件實例對象而盡量復用已有的檢測條件。

it('should call handleUpdateA11y when activeIdx changes', () => {const updateA11ySpy = jest.spyOn(incorrectlyUseInstancePattern, 'handleUpdateA11y');const {incorrectlyUseInstancePattern} = renderWithInstance({extendingClass: AlertModal,props: getProps()});incorrectlyUseInstancePattern.componentDidUpdate({}, { activeIdx: 1 });expect(updateA11ySpy).toHaveBeenCalled();
});作者:隔壁正在裝修真羨慕
鏈接:https://juejin.cn/post/7260024054066085946
來源:稀土掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

時間和成本開銷

Tubi 前端團隊從?Enzyme 到 RTL 的測試遷移工作耗時約兩個半月。期間,我們創建并完成了 109 個與之相關的開發任務(stories),共遷移了 466 個測試文件和 10?余萬行的測試代碼,項目燃盡圖如下。

burndown.jpg

其中,綠色表示 Stories 完成的統計出現驟減,是因為在 Shortcut 系統中,只有對應的代碼真正發布到生產環境后才被統計為完成;但實際的遷移工作每天都在推進,而非一蹴而就。

通過燃盡圖,我們可以看到 Tubi 從 2022 年 9 月已經開始考慮這一遷移工作。當時只是創建了用于概念驗證的前期準備工作和團隊內部的分享討論。11 月底,借著圣誕和新年期間線上代碼凍結的時機,我們正式將測試遷移排上日程。

2022 年 11 月底到 12 月中旬,我們投入了近一個半的全職前端工程師(兩位負責遷移項目的前端工程師,每人投入約 70% 的時間推進遷移工作),構建輔助遷移的 Codemods 、進度報告腳本和 Lint 工具。同時,他們還嘗試手動遷移代碼庫中的一些典型測試,以便了解遷移的難點并找到對應的解決方案。

2022 年 12 月底,隨著輔助工具的完善和對遷移難度的掌控,我們對其他長期工作于 Tubi 產品前端代碼倉庫的工程師進行了 RTL 測試和遷移方面的培訓。之后,將需要遷移的測試文件按對所涉及代碼的熟悉程度和工作量進行了統一分配。2023 年 2 月中旬,我們實際已經完成了全部的遷移工作,燃盡圖在 2 月底上線時將已經合并的測試代碼記為完成。

經驗和總結

大多數工程師(尤其是前端工程師)希望緊追技術潮流,使用最新最酷的技術,但對于已有代碼倉庫而言,框架和依賴遷移的成本不容忽視。成熟的工程團隊需要平衡利弊、明確投入產出比和必要性后做出理智的決策。推進遷移時,尋求漸進而為的方式應被視為基本策略。當然,對新舊代碼分而治之的差異化對待經常是為了快速推進而做出的必要妥協。

另外,本文中提到的 Codemods 和進度報告腳本等工具也都是輔助遷移的必要前期開發。考慮到遷移往往是一次性的工作,因此對輔助工具開發上的投入,我們認為二八原則依然適用,應盡可能花少量時間滿足多數需求,無需做到面面俱到。

如果有機會重新主導一次 RTL 測試遷移項目,以下兩點依然有改進空間:

1. Codemods 的范圍需要提前界定

雖然我們一直在強調二八原則和避免在一次性的 Codemods 工具上做過度投入,但工程師追求完善自動化工具的天性讓我們依然在 Codemods 上花費了比預期更多的時間,實現了對相對不常見的代碼模式及其變形的支持。預先引入團隊計劃和評估決策流程,將有效地避免這一問題。

2. 理應更早引入團隊參與,加速項目進展

在項目后期,我們等 Codemods 相對穩定后才請更多團隊成員參與進來。回頭看,這種瀑布式的開發流程,使整個項目周期拉長了。更好的做法是我們預先確定測試遷移的典型代碼模式,并對團隊做預先培訓。即便 Codemods 沒有完全完成,我們依然可以動員整個前端團隊,盡早開始手動遷移那些 Codemods 計劃中不會支持的代碼模式,做到項目的并行推進。

總體而言,此次測試遷移工作為 Tubi 前端團隊掃清了之后升級最新 React 18 的障礙。在短短兩個月,我們完成了 466 個測試文件和 10 余萬行測試代碼的遷移,這無疑是一個值得稱道的成績。同時,受益于 React Testing Library,團隊實現 UI 測試代碼的效率和對 Web Accessbility 的重視得到了極大提升,這也印證了我們對技術項目投入的價值與收益。

作者:Nick QI,Tubi Staff Software Engineer

歡迎加入 Tubi

如果你對類似項目感興趣,歡迎加入 Tubi!?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/36407.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/36407.shtml
英文地址,請注明出處:http://en.pswp.cn/news/36407.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CentOS-6.3安裝MySQL集群

安裝要求 安裝環境&#xff1a;CentOS-6.3 安裝方式&#xff1a;源碼編譯安裝 軟件名稱&#xff1a;mysql-cluster-gpl-7.2.6-linux2.6-x86_64.tar.gz 下載地址&#xff1a;http://mysql.mirror.kangaroot.net/Downloads/ 軟件安裝位置&#xff1a;/usr/local/mysql 數據存放位…

達夢數據庫(dm8) Centos7 高可用集群

國產數據庫-達夢 一、環境詳情二、Centos7 參數優化&#xff08;所有節點&#xff09;三、創建用戶&#xff08;所有節點&#xff09;四、開始安裝&#xff08;所有節點&#xff09;五、服務注冊啟動 當前安裝&#xff1a;在指定版本環境下 測試&#xff0c;僅供參考 官網描述&…

風丘科技將亮相 EVM ASIA 2023

風丘科技將首次亮相 EVM ASIA 2023 WINDHILL will debut EVM ASIA 2023 ——可持續移動的未來 —The Future of SUSTAINABLE Mobility EVM ASIA 2023是亞太地區電氣化的國際性展會&#xff0c;專注于新能源汽車、充電技術及汽車零件制造等。展會致力于促進包括充電站、交通…

[系統安全] 五十二.DataCon競賽 (1)2020年Coremail釣魚郵件識別及分類詳解

您可能之前看到過我寫的類似文章,為什么還要重復撰寫呢?只是想更好地幫助初學者了解病毒逆向分析和系統安全,更加成體系且不破壞之前的系列。因此,我重新開設了這個專欄,準備系統整理和深入學習系統安全、逆向分析和惡意代碼檢測,“系統安全”系列文章會更加聚焦,更加系…

InnoDB文件物理結構解析5 - FIL_PAGE_INDEX

本文討論FIL_PAGE_INDEX頁的可回收垃圾記錄(Garbage/Deleted Records)&#xff0c;當我們刪除某一條記錄(delete from …)時&#xff0c;通常InnoDB并不會在物理存儲上進行完全刪除&#xff0c;而是在記錄上置一個刪除標志位&#xff0c;我們稱這些行記錄為垃圾記錄&#xff0c…

嵌入式Qt開發—Excel表格數據導出

有一個嵌入式Excel表格數據導出的需求&#xff1a;應用軟件運行于嵌入式Linux平臺上&#xff0c;在設備運行過程中&#xff0c;存儲了許多數據&#xff0c;這些數據想以表格的形式導出。考慮到Windows平臺的普遍性&#xff0c;需要將數據以excel表格形式導出&#xff0c;故選擇…

python庫打包

一、背景 想讓自己寫的python庫可以使用pip install xxx安裝。 二、環境準備 注冊PYPI賬號已經寫好的能正常使用的庫/方法/項目&#xff08;可以本地調用&#xff09;安裝依賴庫setuptools和twinw pip install setuptools pip install twine # 簡化將庫發布到PYPI流程的工…

“中國軟件杯”飛槳賽道晉級決賽現場名單公布

“中國軟件杯”大學生軟件設計大賽是由國家工業和信息化部、教育部、江蘇省人民政府共同主辦&#xff0c;是全國軟件行業規格最高、最具影響力的國家級一類賽事&#xff0c;為《全國普通高校競賽排行榜》榜單內賽事。今年&#xff0c;組委會聯合百度飛槳共同設立了“智能系統設…

C++11之后的C++標準特性宏定義方便功能特性測試

C是一個龐大的編程語言體系&#xff0c;它的高效性是可以直接連接硬件系統&#xff0c;它的靈活性是不斷迭代完善的通用語義機制&#xff0c;當下C的發展演進可謂一路狂奔。不同應用中需要知道C對應的平臺或者版本的功能特性&#xff0c;標準庫信息、C編譯器特性等&#xff0c;…

基于PHP的輕量級博客typecho

本文完成于 5 月中旬&#xff0c;發布時未在最新版本上驗證&#xff1b; 什么是 typecho &#xff1f; Typecho 是一款基于 PHP 的博客軟件&#xff0c;旨在成為世界上最強大的博客引擎。Typecho 在 GNU 通用公共許可證 2.0 下發布。支持多種數據庫&#xff0c;原生支持 Markdo…

24屆近5年南京大學自動化考研院校分析

今天給大家帶來的是南京大學控制考研分析 滿滿干貨&#xff5e;還不快快點贊收藏 一、南京大學 學校簡介 南京大學是一所歷史悠久、聲譽卓著的高等學府。其前身是創建于1902年的三江師范學堂&#xff0c;此后歷經兩江師范學堂、南京高等師范學校、國立東南大學、國立第四中…

JS 刪除的是最后一頁的最后一條,頁碼設置邏輯

刪除的場景&#xff1a; 解決思路&#xff1a; 1、計算操作后的總頁數 2、刪除成功之后的總頁數與當前總頁數進行比較 3、如果刪除成功之后的總頁數比小于當前總頁數&#xff0c;需要把當前頁碼減去1&#xff1b;否則&#xff0c;直接進行列表數據的請求 代碼實現 /*總條數…

VBA 學習筆記1 對象以及屬性

目錄 1 取得VBA對象1.1 取得工作簿對象1.2 取得工作表對象1.3 取得單元格對象1.4 取得對象的屬性1.5 文檔的方法1 進入vba 界面 方式之一&#xff1a; 快捷鍵&#xff1a;ALTERF11 運行方式之一&#xff1a; 進入vba界面&#xff0c;點擊綠色三角符號 1 取得VBA對象 1.1 取得…

DAY21

題目一 給定三個字符串str1、str2和aim&#xff0c; 如果aim包含且僅包含來自str1和str2的所有字符&#xff0c;而且在aim中屬于str1的字符 之間保持原來在str1中的順序&#xff0c;屬于str2的字符之間保持原來在str2中的順序&#xff0c;那么稱aim是str1和str2的交錯組成。實…

Springboot-Retrofit HTTP工具框架快速使用

在SpringBoot項目直接使用okhttp、httpClient或者RestTemplate發起HTTP請求&#xff0c;既繁瑣又不方便統一管理。 因此&#xff0c;在這里推薦一個適用于SpringBoot項目的輕量級HTTP客戶端框架retrofit-spring-boot-starter&#xff0c;使用非常簡單方便&#xff0c;同時又提供…

約數個數(質因子分解)

思路&#xff1a; &#xff08;1&#xff09;由數論基本定理&#xff0c;任何一個正整數x都能寫作&#xff0c;其中p1,p2..pk為x的質因子。 &#xff08;2&#xff09;由此可以推斷&#xff0c;要求一個數約數的個數&#xff0c;注意到約數就是p1,p2...pk的一種組合&#xff…

日常BUG—— SpringBoot項目DEBUG模式啟動慢、卡死。

&#x1f61c;作 者&#xff1a;是江迪呀??本文關鍵詞&#xff1a;日常BUG、BUG、問題分析??每日 一言 &#xff1a;存在錯誤說明你在進步&#xff01; 一、問題描述 我們調試程序時&#xff0c;需要使用DEBUG模式啟動SpringBoot項目&#xff0c; 有時候會發…

convert Auto-Login (cwallet.sso) Wallet into a PKCS12 compliant Wallet

一步不行嗎 &#xff1f; 1. If $JAVA_HOME is not set: a)For FMW 11g components associated with a WebLogic Domain or a FMW 12c Collocated OHS install run: $MIDDLEWARE_HOME/user_projects/domains/<domain>/bin/setDomainEnv.sh b) For FMW 11g Standalone…

側滑置頂,取消置頂

第一步:布局 <?xml version"1.0" encoding"utf-8"?> <com.ddmh.magic.camera.ui.widget.SwipeMenuLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto"…

SQL | 使用通配符進行過濾

6-使用通配符進行過濾 6.1-LIKE操作符 前面介紹的所有操作符都是通過已知的值進行過濾&#xff0c;或者檢查某個范圍的值。但是如果我們想要查找產品名字中含有bag的數據&#xff0c;就不能使用前面那種過濾情況。 利用通配符&#xff0c;可以創建比較特定數據的搜索模式。 …