paint-brush
플래키 테스트의 네 기수~에 의해@truuts
1,276 판독값
1,276 판독값

플래키 테스트의 네 기수

~에 의해 Eugene Truuts6m2023/10/24
Read on Terminal Reader

너무 오래; 읽다

불안정한 자동화 테스트는 QA 엔지니어에게 골칫거리가 되어 불확실성을 초래하고 테스트 스위트의 신뢰성을 약화시킬 수 있습니다. SDET 및 QA 자동화 멘토로서의 경험을 바탕으로 이 기사에서는 결함을 극복하기 위한 실용적인 조언을 제공합니다. JavaScript 및 Playwright 예제를 제공하겠지만 이러한 보편적인 팁은 모든 언어 및 프레임워크에 적용 가능하므로 강력하고 신뢰할 수 있는 자동화 테스트를 작성하는 데 도움이 됩니다. 자세히 알아보고 테스트가 확고하게 유지되는지 확인해 보세요.
featured image - 플래키 테스트의 네 기수
Eugene Truuts HackerNoon profile picture
0-item
1-item


불안정한 자동화 테스트는 QA 엔지니어에게 골칫거리가 되어 불확실성을 초래하고 테스트 스위트의 신뢰성을 약화시킬 수 있습니다. SDET 및 QA 자동화 멘토로서의 경험을 바탕으로 이 기사에서는 결함을 극복하기 위한 실용적인 조언을 제공합니다. JavaScript 및 Playwright 예제를 제공하겠지만 이러한 보편적인 팁은 모든 언어 및 프레임워크에 적용 가능하므로 강력하고 신뢰할 수 있는 자동화 테스트를 작성하는 데 도움이 됩니다. 본격적으로 테스트를 진행하여 테스트가 확고하게 유지되는지 확인해 보세요.

1. 암시적인 대기 방법을 피하세요

때때로 테스트 시나리오를 진행하기 위해 요소가 DOM에 나타날 때까지 또는 애플리케이션이 특정 상태에 도달할 때까지 기다려야 하는 상황에 직면하게 됩니다. Playwright의 자동 대기 기능과 같은 최신 지능형 자동화 프레임워크를 사용하더라도 사용자 정의 대기 방법을 구현해야 하는 경우가 있습니다. 예를 들어 텍스트 필드와 확인 버튼이 있는 시나리오를 생각해 보세요. 특정 서버측 동작으로 인해 확인 버튼은 양식을 작성한 후 약 5초 후에만 표시됩니다. 이러한 경우 두 테스트 단계 사이에 5초의 암시적 대기 시간을 삽입하려는 유혹을 물리치는 것이 필수적입니다.


 textField.fill('Some text') waitForTime(5000) confirmationButton.click()


대신 현명한 접근 방식을 사용하고 정확한 상태를 기다리세요. 일부 로딩 후에 나타나는 텍스트 요소일 수도 있고, 현재 단계가 성공적으로 완료된 후 사라지는 로더 회전 요소일 수도 있습니다. 다음 테스트 단계에 대한 준비가 되었는지 확인할 수 있습니다.


 button.click() waitFor(textField.isVisible()) textField.fill()


물론 현명하게 확인할 수 없는 일을 기다려야 하는 경우도 있습니다. 그러한 위치에 주석을 추가하거나 대기 메소드 또는 이와 유사한 매개변수로 이유 설명을 추가하는 것이 좋습니다.


 waitForTime(5000, {reason: "For unstable server processed something..."}


이를 사용하는 추가 이익은 그러한 설명을 저장할 수 있는 테스트 보고서이므로 다른 사람이나 미래의 당신에게도 여기서 5초 동안 무엇을, 왜 기다리는지 분명하게 알 수 있습니다.


 waitForTime(5000, {reason: "For unstable server processed something..."} button.click() waitFor(textField.isVisible()) textField.fill()

2. 강력하고 신뢰할 수 있는 로케이터를 사용하여 요소를 선택합니다.

로케이터는 자동화 테스트의 중요한 부분이며 모두가 알고 있습니다. 그러나 이 단순한 사실에도 불구하고 많은 자동화 엔지니어는 안정성을 무시하고 테스트에 이와 같은 것을 사용하고 있습니다.


 //*[@id="editor_7"]/section/div[2]/div/h3[2]


DOM 구조가 그다지 정적이지 않기 때문에 이는 어리석은 접근 방식입니다. 일부 다른 팀에서는 때때로 이를 변경할 수 있으며 이는 테스트가 실패한 후에 발생합니다. 따라서 강력한 로케이터를 사용하십시오. 그런데 내 XPath 관련 이야기를 읽어주신 것을 환영합니다.



3. 테스트를 서로 독립적으로 만듭니다.

단일 스레드에서 여러 테스트로 테스트 스위트를 실행할 때 각 테스트가 독립적인지 확인하는 것이 중요합니다. 이는 첫 번째 테스트에서 시스템을 원래 상태로 되돌려 다음 테스트가 예상치 못한 조건으로 인해 실패하지 않도록 해야 함을 의미합니다. 예를 들어 테스트 중 하나가 LocalStorage에 저장된 사용자 설정을 수정하는 경우 첫 번째 테스트 실행 후 사용자 설정에 대한 LocalStorage 항목을 지우는 것이 좋은 방법입니다. 이렇게 하면 두 번째 테스트가 기본 사용자 설정으로 실행됩니다. 또한 각각의 새 테스트가 동일한 초기 조건으로 시작되도록 페이지를 기본 항목 페이지로 재설정하고 쿠키 및 데이터베이스 항목을 지우는 등의 작업을 고려할 수도 있습니다.


예를 들어 Playwright에서는 beforeAll(), beforeEach(), afterAll() 및 afterEach() 주석을 사용하여 이를 달성할 수 있습니다.


몇 가지 테스트를 통해 다음 테스트 스위트를 확인하세요. 첫 번째는 사용자의 외모 설정을 변경하는 것이고, 두 번째는 기본 외모를 확인하는 것입니다.


 test.describe('Test suite', () => { test('TC101 - update appearance settings to dark', async ({page}) => { await user.goTo(views.settings); await user.setAppearance(scheme.dark); }); test('TC102 - check if default appearance is light', async ({page}) => { await user.goTo(views.settings); await user.checkAppearance(scheme.light); }); });


이러한 테스트 모음이 병렬로 실행되면 모든 것이 원활하게 진행됩니다. 그러나 이러한 테스트를 하나씩 실행하면 그 중 하나가 다른 테스트를 방해하므로 테스트가 실패할 수 있습니다. 첫 번째 테스트에서는 외관이 밝음에서 어두움으로 변경되었습니다. 두 번째 테스트에서는 현재 모습이 밝은지 확인합니다. 그러나 첫 번째 테스트에서 기본 모양이 이미 변경되었으므로 실패합니다.

이러한 문제를 해결하기 위해 각 테스트 후에 실행되는 afterEach() 후크를 추가했습니다. 이 경우 기본 보기로 이동하고 로컬 저장소에서 사용자의 모양을 제거하여 사용자의 모양을 지웁니다.


 test.afterEach(async ({ page }) => { await user.localStorage(appearanceSettings).clear() await user.goTo(views.home) }); test.describe('Test suite', () => { test('TC101 - update appearance settings to dark', async ({page}) => { await user.goto(views.settings); await user.setAppearance(scheme.dark); }); test('TC102 - check if default appearance is light', async ({page}) => { await user.goTo(views.settings); await user.checkAppearance(scheme.light); }); });


이 접근 방식을 사용하면 이 제품군의 각 테스트가 독립적이 됩니다.


4. 자동 재시도를 현명하게 사용하세요

불안정한 테스트에 면역이 있는 사람은 아무도 없으며, 이 문제에 대한 인기 있는 해결 방법은 재시도를 사용하는 것입니다. CI/CD 구성 수준에서도 재시도가 자동으로 발생하도록 구성할 수 있습니다. 예를 들어, 실패한 각 테스트에 대해 자동 재시도를 설정하고 최대 재시도 횟수를 3번으로 지정할 수 있습니다. 처음에 실패한 테스트는 테스트 실행 주기가 완료될 때까지 최대 3번까지 재시도됩니다. 장점은 테스트가 약간 불안정한 경우 몇 번의 재시도 후에 통과할 수 있다는 것입니다.


그러나 단점은 실패하여 추가 런타임 소비가 발생할 수 있다는 것입니다. 더욱이, 이 방법은 두 번째 또는 세 번째 시도에서 많은 테스트가 "통과"한 것처럼 보일 수 있고 안정적이라고 잘못 표시할 수 있기 때문에 의도치 않게 불안정한 테스트가 누적될 수 있습니다. 따라서 전체 테스트 프로젝트에 대해 전역적으로 자동 재시도를 설정하지 않는 것이 좋습니다. 대신, 테스트의 기본 문제를 즉시 해결할 수 없는 경우 선택적으로 재시도를 사용하십시오.


예를 들어 'filechooser' 이벤트를 사용하여 일부 파일을 업로드해야 하는 테스트 사례가 있다고 가정해 보겠습니다. <'filechooser' 이벤트를 기다리는 동안 시간 제한이 초과되었습니다> 오류가 발생합니다. 이는 Playwright 테스트가 'filechooser' 이벤트가 발생하기를 기다리고 있지만 시간 제한이 더 오래 걸리고 있음을 나타냅니다.


 async function uploadFile(page: Page, file) { const fileChooserPromise = page.waitForEvent('filechooser'); await clickOnElement(page, uploadButton); const fileChooser = await fileChooserPromise; await fileChooser.setFiles(file); }


이는 애플리케이션의 예상치 못한 동작이나 느린 환경 등 다양한 이유 때문일 수 있습니다. 이는 무작위 동작이므로 가장 먼저 생각할 수 있는 것은 이 테스트에 자동 재시도를 사용하는 것입니다. 그러나 전체 테스트를 재시도하면 추가 시간을 낭비하게 되고 uploadFile() 메서드 자체에서 첫 번째 오류가 발생한 것과 동일한 동작이 발생할 위험이 있으므로 오류가 발생하더라도 전체 테스트를 재시도할 필요가 없습니다.


이를 위해 표준 try…catch 문을 사용할 수 있습니다.


 async function uploadFile(page: Page, file) { const maxRetries = 3; let retryCount = 0; while (retryCount < maxRetries) { try { const fileChooserPromise = page.waitForEvent('filechooser'); await clickOnElement(page, uploadButton); const fileChooser = await fileChooserPromise; await fileChooser.setFiles(file); break; // Success, exit the loop } catch (error) { console.error(`Attempt ${retryCount + 1} failed: ${error}`); retryCount++; } } }


이러한 접근 방식을 사용하면 추가 시간을 절약하고 테스트의 불안정성을 줄일 수 있습니다.


마지막으로, 신뢰할 수 있는 자동화 테스트를 향한 길은 간단합니다. 일반적인 문제를 최소화하거나 적극적으로 해결하고 지능적인 전략을 구현할 수 있습니다. 이 여정은 계속 진행 중이며, 지속성을 통해 테스트의 신뢰성이 더욱 높아질 것임을 기억하세요. 허술함에 작별을 고하고 안정성을 환영합니다. 품질에 대한 귀하의 헌신은 프로젝트 성공을 보장합니다. 즐거운 테스트 되세요!


여기에도 게시되었습니다.