asp.net網(wǎng)站開發(fā)流程手機維修培訓(xùn)班學(xué)校
?
?
😏作者簡介:博主是一位測試管理者,同時也是一名對外企業(yè)兼職講師。
📡主頁地址:【Austin_zhai】
🙆目的與景愿:旨在于能幫助更多的測試行業(yè)人員提升軟硬技能,分享行業(yè)相關(guān)最新信息。
💎聲明:博主日常工作較為繁忙,文章會不定期更新,各類行業(yè)或職場問題歡迎大家私信,有空必回。

?
?
閱讀目錄
- 1. 接上回
- 2. 自定義命令
- 2.1 參數(shù)傳遞
- 2.2 鏈?zhǔn)秸{(diào)用
- 2.3 自定義斷言
- 2.4 處理異步操作
- 2.5 Cypress對象
- 3. 注意點
- 3.1 關(guān)于腳本業(yè)務(wù)上下文
- 3.2 抽象的程度
1. 接上回
??上一篇我們介紹了一些Cypress中的一些高頻使用技巧,那么今天就由博主我繼續(xù)來為大家?guī)黻P(guān)于Cypress的一些高階技巧。
?
?
2. 自定義命令
??在Cypress中,自定義命令是一個強大的輔助功能,說直白點就是它允許你將重復(fù)使用的代碼片段抽象成可重用的命令。而通過這些自定義的命令,我們可以讓我們的自動化測試腳本更加的趨于模塊化,可想而知的是,模塊化的腳本其自身的可維護性、復(fù)用性和可閱讀性就會更上一個臺階。
??要使用自定義命令,我們就需要在support/commands.js中建立自己的命令。比如我們需要將登錄這個業(yè)務(wù)動作進行抽象,那就先編寫一段登錄的相關(guān)業(yè)務(wù)代碼。
我們寫一個十分簡單的登錄操作,語法如下,可以看到整個的業(yè)務(wù)代碼十分的簡單,只需要將用戶名和密碼進行傳參即可。
Cypress.Commands.add('login', (username, password) => {cy.visit('/login');cy.get('#username').type(username);cy.get('#password').type(password);cy.get('button[type="submit"]').click();
});
??那么我們在commands.js中將這段業(yè)務(wù)代碼添加完成后,在實際的測試腳本中就可以直接對其進行使用。
使用起來是不是很方便,因為其本身就是將業(yè)務(wù)方法繼續(xù)抽象,所以直接調(diào)用其方法名就可以達到登錄代碼同樣的效果。
describe('login_test', () => {it('should log in successfully', () => {cy.login('your_username', 'your_password');});
});
?
2.1 參數(shù)傳遞
??我們在定義業(yè)務(wù)方法的時候傳參不僅僅可以傳一些基礎(chǔ)的業(yè)務(wù)參數(shù),還可以在此基礎(chǔ)上根據(jù)自己的業(yè)務(wù)場景來定義一些比較靈活的參數(shù)類別。比如我們在對特定元素進行業(yè)務(wù)操作時,我們可以統(tǒng)一的定義一個操作類或方法,來對此進行特定的傳參,類似于selenium中find_elelment方法。
??我們先在commands.js中定義,這里我們要傳遞的參數(shù)是一個元素選擇器。這樣我們就可以靈活的在頁面上選擇到任何一個能捕捉到的元素。
Cypress.Commands.add('clickAndVerify', { prevSubject: 'element' }, (element, text) => {cy.wrap(element).click();cy.contains(text);
});
??使用的時候只需要直接調(diào)用即可。
cy.get('.my-button').clickAndVerify('Clicked Button Text');
?
2.2 鏈?zhǔn)秸{(diào)用
??自定義命令毫無意外的也支持了鏈?zhǔn)綄懛?#xff0c;無疑這讓我們在設(shè)計腳本的過程中可以更加靈活的應(yīng)對各類復(fù)雜業(yè)務(wù)場景。
??同樣的現(xiàn)在commands.js中定義,這里我們在返回get的時候進行了鏈?zhǔn)秸{(diào)用。
Cypress.Commands.add('login', (username, password) => {cy.visit('/login');cy.get('#username').type(username);cy.get('#password').type(password);cy.get('button[type="submit"]').click();return cy.get('.user-dashboard');
});
??使用的時候只需要直接調(diào)用即可。
cy.login('your_username', 'your_password').should('be.visible');
?
2.3 自定義斷言
??同樣的,既然可以進行抽象,我們也完全可以將斷言的操作加進自定義命令,以驗證特定的狀態(tài)或條件,包括一些特殊的驗證邏輯。
??commands.js中定義,斷言元素存在切包含text。
Cypress.Commands.add('shouldBeVisibleAndContain', { prevSubject: 'element' }, (element, text) => {cy.wrap(element).should('be.visible').and('contain', text);
});
??直接調(diào)用方法即可對元素進行斷言。
cy.get('.my-element').shouldBeVisibleAndContain('Expected Text');
?
2.4 處理異步操作
??對于上一篇末尾處說到的異步操作處理,同樣可以在自定義命令中進行抽象,其實在被測對象中異步操作是很常見的,比如等待某個條件成立后再繼續(xù)執(zhí)行后續(xù)的操作,類似的這種場景我們都可以在自定義命令中繼續(xù)抽象和服用,以優(yōu)化腳本的整體運行效率和維護性。
??在commands.js中定義,等待特定的條件后再執(zhí)行后續(xù)的操作。
Cypress.Commands.add('waitForApiResponse', () => {cy.intercept('GET', '/api/data').as('apiCall');cy.wait('@apiCall');
});
??調(diào)用,不再贅述。
cy.waitForApiResponse();
?
2.5 Cypress對象
??除了以上說的這些方法外,我們還可以將一些元素和值包裝成Cypress對象,這樣做的作用就是讓這些抽象后的對象可以在自定義命令中使用更多的Cypress自帶命令。
??在commands.js中定義,我們使用cy.wrap()將對象包裝成Cypress對象,使用自帶的日志命令。
Cypress.Commands.add('logAndDebug', (subject) => {cy.wrap(subject).debug().log('Subject:', subject);
});
??調(diào)用,不再贅述。
cy.get('.my-element').logAndDebug();
?
?
3. 注意點
??我們在使用自定義命令的同時也需要注意一些特殊的情況與場景。
?
3.1 關(guān)于腳本業(yè)務(wù)上下文
??在自定義命令中,當(dāng)然也存在著上下文的關(guān)系,我們要確保了解Cypress中命令的上下文,其中this
與prevSubject
是特別覺有代表性的關(guān)鍵字。它們其實是允許你在自定義命令中引用和操作前一個命令的主體,就this
這個來說,它在自定義命令中用于引用當(dāng)前命令的上下文,對于一般的命令,它指向cy對象;對于一些帶有{ prevSubject: 'element' }
選項的命令,this
則會指向前一個命令的主體,這個是需要大家注意的。
??下面我們來舉兩個例子:
??首先我們來看普通命令中的this
,這里的this
就是指向cy對象的。
Cypress.Commands.add('customCommand', function () {cy.log(this);
});
??調(diào)用
cy.customCommand();
??而對面帶有{ prevSubject: 'element' }
的方法時,這里的this
就像我之前說的那樣,指向的是前一個命令的主體。簡單點來說this
指向前一個命令的subject,而cy.log(subject)里的就是前一個命令的主體。
Cypress.Commands.add('customCommandWithSubject', { prevSubject: 'element' }, function (subject) {cy.log(this); cy.log(subject);
});
??調(diào)用
cy.get('.my-element').customCommandWithSubject();
?
??prevSubject
的用作為告訴cypress你的自定義命令期望前一個命令的主體作為傳參,一般在多個自定義命令中共享同一個元素的場景中會頻繁使用到。
??同理,這里我們對前一個命令的主體進行點擊操作,所以使用prevSubject
來達到我們所想要的效果。
Cypress.Commands.add('customCommandWithSubject', { prevSubject: 'element' }, function (subject) {cy.wrap(subject).click();
});
??調(diào)用
cy.get('.my-element').customCommandWithSubject();
?
3.2 抽象的程度
??雖然在自定義命令中我們需要對要定義的方法進行抽象,但往往會有些同學(xué)在設(shè)計的過程中什么都想要,從而導(dǎo)致自己的自定義命令變得過度抽象,這些代碼的可讀性一般都比較差而且維護起來難度較大,無法適應(yīng)被測對象界面中的需求更改與樣式變更。
??這里我們就舉一個過度抽象的例子,讓大家了解適度和過度抽象的區(qū)別。
??我們先來看一下過度抽象的自定義命令,這里雖然方法中提供了一個登錄的基本步驟,但它的步驟過于具體,這樣會導(dǎo)致在測試用例中要添加其他的測試邏輯變得困難,本身自定義命令的本質(zhì)就是用來大量復(fù)用的,這樣就變得本末倒置了。所以這樣的抽象程度限制了自定義命令的靈活性,使得它本身的價值變得可有可無。
Cypress.Commands.add('login', (username, password) => {cy.visit('/login');cy.get('#username').type(username);cy.get('#password').type(password);cy.get('button[type="submit"]').click();cy.get('.user-dashboard').should('be.visible');
});
??調(diào)用
describe('Login Test', () => {it('should log in successfully', () => {cy.login('testuser', 'password123');});
});
??那么接下來我們看一下什么是適度抽象的自定義命令,下面這段乍一看似乎與上面的沒什么很大的區(qū)別,其實則不然。在這其中我們只保留的基本的登錄操作,不進行過多的細(xì)化操作,說人話就是我們只把共通與大框架的部分保留了下來,一些根據(jù)業(yè)務(wù)不同而擴展或特定的操作則被丟棄掉了。這樣我們就可以在測試用例中添加更多的具體步驟來適應(yīng)各類業(yè)務(wù)測試場景的需求。
Cypress.Commands.add('basicLogin', (username, password) => {cy.visit('/login');cy.get('#username').type(username);cy.get('#password').type(password);cy.get('button[type="submit"]').click();
});
??調(diào)用
describe('Login Test', () => {it('should log in successfully', () => {cy.basicLogin('testuser', 'password123');cy.get('.user-dashboard').should('be.visible');});
});