paint-brush
안정적인 소프트웨어: 자동화된 테스트의 힘에 대해 알아보세요~에 의해@igorluchenkov
1,022 판독값
1,022 판독값

안정적인 소프트웨어: 자동화된 테스트의 힘에 대해 알아보세요

~에 의해 Igor Luchenkov6m2024/03/02
Read on Terminal Reader

너무 오래; 읽다

자동화된 테스트는 너무 많은 시간을 들이지 않고 소프트웨어를 테스트하는 방법입니다. 테스트에는 단위, 통합, 엔드투엔드의 세 가지 유형이 있습니다. 모든 종류에 대해 자세히 알아보고 각 종류가 필요한 이유를 이해해 보겠습니다. 단위 테스트는 빠릅니다. 격리를 사용하면 종속 서비스를 실행하거나 API 및 데이터베이스를 호출하지 않고도 로컬 및 CI에서 언제든지 실행할 수 있습니다. 통합 테스트는 속도가 느리지만 앱의 일부가 연결되는 방식을 테스트합니다.
featured image - 안정적인 소프트웨어: 자동화된 테스트의 힘에 대해 알아보세요
Igor Luchenkov HackerNoon profile picture

이 기사는 주목할 가치가 있습니다.

  • 좋은 품질의 소프트웨어를 작성하는 데 열정을 갖고 있으며 테스트를 통해 앱의 안정성을 향상시키고 싶습니다.


  • 프로덕션 시스템에 예상치 못한 버그가 나타나는 것에 지쳤습니다.


  • 자동화된 테스트가 무엇인지, 그리고 어떻게 접근하는지 이해하는 데 도움이 필요합니다.

자동화된 테스트가 필요한 이유는 무엇입니까?

엔지니어로서 우리는 작동하는 것을 만들고 싶지만 새로운 기능을 만들 때마다 필연적으로 앱의 크기와 복잡성이 증가합니다.


제품이 성장함에 따라 변경 사항의 영향을 받는 모든 기능을 수동으로 (예: 손으로) 테스트하는 데 점점 더 많은 시간이 소요됩니다.


자동화된 테스트가 없으면 너무 많은 시간을 소비하여 배송 속도가 느려지거나 속도를 절약하는 데 너무 적은 비용을 소비하게 되어 PagerDuty의 심야 호출과 함께 백로그에 새로운 버그가 발생하게 됩니다.


반대로, 컴퓨터는 동일한 작업을 반복적으로 수행하도록 프로그래밍할 수 있습니다 . 그러니 테스트를 컴퓨터에 위임하자!


테스트 유형

테스트 피라미드


테스트 피라미드 아이디어는 단위, 통합 및 엔드투엔드의 세 가지 주요 테스트 유형을 제안합니다. 모든 종류에 대해 자세히 알아보고 각 종류가 필요한 이유를 이해해 보겠습니다.

단위 테스트

유닛은 (다른 구성 요소에 의존하지 않고) 독립적 으로 테스트하는 작은 논리 조각입니다.

단위 테스트는 빠릅니다. 몇 초 안에 완료됩니다. 격리를 사용하면 종속 서비스를 실행하거나 API 및 데이터베이스를 호출하지 않고도 로컬 및 CI에서 언제든지 실행할 수 있습니다.


단위 테스트 예: 두 숫자를 허용하고 이를 합산하는 함수입니다. 우리는 이를 다른 인수로 호출하고 반환된 값이 올바른지 확인하고 싶습니다.


 // Function "sum" is the unit const sum = (x, y) => x + y test('sums numbers', () => { // Call the function, record the result const result = sum(1, 2); // Assert the result expect(result).toBe(3) }) test('sums numbers', () => { // Call the function, record the result const result = sum(5, 10); // Assert the result expect(result).toBe(15) })


더 흥미로운 예는 API 요청이 완료된 후 일부 텍스트를 렌더링하는 React 구성 요소입니다. 테스트에 필요한 값을 반환하고, 구성 요소를 렌더링하고, 렌더링된 HTML에 필요한 콘텐츠가 있는지 확인하기 위해 API 모듈을 모의해야 합니다.


 // "MyComponent" is the unit const MyComponent = () => { const { isLoading } = apiModule.useSomeApiCall(); return isLoading ? <div>Loading...</div> : <div>Hello world</div> } test('renders loading spinner when loading', () => { // Mocking the API module, so that it returns the value we need jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Loading...').toBeInTheDocument() }) test('renders text content when not loading', () => { // Mocking the API module jest.mock(apiModule).mockReturnValue(() => ({ useSomeApiCall: jest.fn(() => ({ // Return "isLoading: false" for this test case isLoading: false })) })) // Execute the unit (render the component) const result = render(<MyComponent />) // Assert the result result.findByText('Hello world').toBeInTheDocument() })


통합 테스트

귀하의 유닛이 다른 유닛(종속성) 과 상호작용할 때 이를 통합 이라고 부릅니다. 이러한 테스트는 단위 테스트보다 느리지만 앱의 일부가 연결되는 방식을 테스트합니다.


통합 테스트 예시: 데이터베이스에 사용자를 생성하는 서비스입니다. 이를 위해서는 테스트 실행 시 DB 인스턴스( 종속성 )를 사용할 수 있어야 합니다. 서비스가 DB에서 사용자를 생성하고 검색할 수 있는지 테스트하겠습니다.


 import db from 'db' // We will be testing "createUser" and "getUser" const createUser = name => db.createUser(name) // creates a user const getUser = name => db.getUserOrNull(name) // retrieves a user or null test("creates and retrieves users", () => { // Try to get a user that doesn't exist, assert Null is returned const nonExistingUser = getUser("i don't exist") expect(nonExistingUser).toBe(null); // Create a user const userName = "test-user" createUser(userName); // Get the user that was just created, assert it's not Null const user = getUser(userName); expect(user).to.not.be(null) })


엔드투엔드 테스트

모든 종속성을 사용할 수 있는 완전히 배포된 앱을 테스트하는 것은 엔드투엔드 테스트입니다. 이러한 테스트는 실제 사용자 행동을 가장 잘 시뮬레이션하고 앱에서 발생할 수 있는 모든 문제를 포착할 수 있게 해주지만 가장 느린 유형의 테스트입니다.


엔드투엔드 테스트를 실행하려면 항상 모든 인프라를 프로비저닝하고 환경에서 타사 제공업체를 사용할 수 있는지 확인해야 합니다.


앱의 중요한 기능을 위해서만 갖고 싶을 뿐입니다 .


엔드투엔드 테스트 예시인 로그인 흐름을 살펴보겠습니다 . 앱으로 이동하여 로그인 세부정보를 입력하고 제출한 후 환영 메시지를 확인하고 싶습니다.


 test('user can log in', () => { // Visit the login page page.goto('https://example.com/login'); // Fill in the login form page.fill('#username', 'john'); page.fill('#password', 'some-password'); // Click the login button page.click('#login-button'); // Assert the welcome message is visible page.assertTextVisible('Welcome, John!') })

작성할 테스트 종류를 어떻게 선택합니까?

엔드투엔드 테스트는 통합보다 느리고 통합 테스트는 단위 테스트보다 느리다는 점을 기억하세요.


작업 중인 기능이 미션 크리티컬한 경우 최소한 하나의 엔드투엔드 테스트 작성을 고려하십시오(예: 인증 흐름을 개발할 때 로그인 기능이 작동하는 방식 확인).


미션 크리티컬 흐름 외에도 우리는 가능한 한 많은 엣지 케이스와 다양한 기능 상태를 테스트하고 싶습니다. 통합 테스트를 통해 앱의 각 부분이 어떻게 함께 작동하는지 테스트할 수 있습니다.


엔드포인트와 클라이언트 구성 요소에 대한 통합 테스트를 수행하는 것은 좋은 생각입니다. 엔드포인트는 작업을 수행하고, 예상한 결과를 생성해야 하며, 예상치 못한 오류가 발생하지 않아야 합니다.


클라이언트 구성 요소는 올바른 콘텐츠를 표시하고 예상되는 응답 방식으로 사용자 상호 작용에 응답해야 합니다.


그리고 마지막으로 단위 테스트는 언제 선택해야 할까요? 숫자를 합하는 sum , <button> 태그를 렌더링하는 Button 개별적으로 테스트할 수 있는 모든 작은 함수는 단위 테스트를 위한 훌륭한 후보입니다. 테스트 주도 개발 접근 방식을 따르면 단위는 완벽합니다.


무엇 향후 계획?

몇 가지 테스트를 작성해 보세요! (하지만 작게 시작하세요)

  • 프로젝트/언어에 적합한 테스트 프레임워크를 설치하세요 . 각 언어에는 Jest / Vitest for JavaScript, Cypress / Playwright for end-to-end(JavaScript도 사용), JUnit for Java 등과 같은 인기 있는 테스트용 라이브러리가 있습니다.


  • 프로젝트에서 작은 함수를 찾아 이에 대한 단위 테스트를 작성하세요.


  • 일부 구성 요소/서비스-데이터베이스 상호 작용에 대한 통합 테스트를 작성합니다.


  • 간단한 로그인 흐름과 같이 빠르게 테스트할 수 있는 중요한 시나리오를 선택하고 이에 대한 엔드투엔드 테스트를 작성하세요.


위의 작업을 한 번 수행하여 작동 방식을 이해하세요. 그런 다음 일부 기능/버그 작업 중에 다시 수행하십시오. 그런 다음 동료들과 공유하여 모두가 테스트를 작성하고, 시간을 절약하고, 밤에 더 잘 수 있도록 하세요!


유용한 자료: