테스트의 정의

소프트웨어를 작성할 때 테스트를 통해 제대로 작동하는지 확인할 수 있습니다. 테스트는 소프트웨어가 의도한 대로 작동하는지 확인하기 위해 특정 방식으로 소프트웨어를 실행하는 프로세스로 광범위하게 정의할 수 있습니다.

테스트를 성공적으로 수행하면 새로운 코드, 기능을 추가하거나 심지어 종속 항목을 업그레이드해도 이미 작성한 소프트웨어가 예상한 대로 계속 작동한다는 확신을 가질 수 있습니다. 테스트는 예상 밖의 시나리오나 예기치 않은 입력으로부터 소프트웨어를 보호하는 데도 도움이 됩니다.

테스트할 수 있는 웹 동작의 예는 다음과 같습니다.

  • 버튼 클릭 시 웹사이트의 기능이 올바르게 작동하는지 확인
  • 복잡한 함수가 올바른 결과를 생성하는지 확인
  • 사용자 로그인이 필요한 작업 완료
  • 잘못된 형식의 데이터가 입력되었을 때 양식에서 오류를 올바르게 보고하는지 확인합니다.
  • 사용자의 대역폭이 매우 낮거나 오프라인 상태가 되더라도 복잡한 웹 앱이 계속 작동하도록 합니다.

자동 및 수동 테스트 비교

소프트웨어를 테스트할 수 있는 일반적인 방법은 두 가지입니다. 자동 테스트와 수동 테스트입니다.

수동 테스트에는 브라우저에서 웹사이트를 로드하고 예상대로 작동하는지 확인하는 등 소프트웨어를 직접 실행하는 사람이 포함됩니다. 수동 테스트는 간단하게 만들거나 정의할 수 있습니다. 예를 들어 사이트가 로드될 수 있는지, 이러한 작업을 수행할 수 있나요? 하지만 실행할 때마다 사람이 엄청나게 많은 시간을 소비해야 합니다. 인간은 매우 창의적이어서 탐색적 테스트라고 하는 테스트를 실행할 수 있지만, 특히 동일한 작업을 여러 번 실행할 때는 실패나 불일치를 알아채지 못할 수 있습니다.

자동 테스트는 사람이 설정 또는 결과 확인과 같은 반복 단계를 수행하지 않고도 컴퓨터에서 테스트를 코드화하고 반복적으로 실행하여 소프트웨어의 의도된 동작을 확인할 수 있는 프로세스입니다. 자동화된 테스트가 구성되고 나면 자주 실행될 수 있다는 점이 중요합니다. 이것은 여전히 매우 광범위한 정의이며 자동화된 테스트는 온갖 형태와 형태를 취합니다. 이 과정의 대부분은 자동화된 테스트를 연습으로 삼는 것과 관련이 있습니다

수동 테스트는 대체로 자동 테스트 작성의 선구자 역할을 하지만, 자동 테스트가 너무 신뢰할 수 없거나 범위가 넓거나 다루기 어려워지는 경우에 유용합니다.

예시를 통한 기본 사항

JavaScript 또는 관련 언어를 작성하는 웹 개발자로서 간결한 자동 테스트는 매일 Node를 통해 실행하거나 브라우저에서 로드하여 실행하는 스크립트일 수 있습니다.

import { fibonacci } from "../src/math.js";

if (fibonacci(0) !== 0) {
  throw new Error("Invalid 0th fibonacci result");
}
const fib13 = fibonacci(13);
if (fib13 !== 233) {
  throw new Error("Invalid 13th fibonacci result, was=${fib13} wanted=233");
}

이 예시는 다음과 같은 유용한 정보를 제공하는 간단한 예시입니다.

  • 이는 일부 소프트웨어 (Fibonacci 함수)를 실행하고 예상 값과 비교하여 결과를 확인하여 동작이 의도대로 작동하는지 확인하기 때문에 테스트입니다. 동작이 올바르지 않으면 자바스크립트가 Error을 발생시켜 표현하는 오류가 발생합니다.

  • 이 스크립트를 터미널이나 브라우저에서 수동으로 실행하더라도 개별 단계를 실행하지 않고도 반복적으로 실행할 수 있으므로 여전히 자동화된 테스트입니다. 다음 페이지인 테스트가 실행되는 위치에서 자세히 설명합니다.

  • 이 테스트는 라이브러리를 사용하지 않고 어디서나 실행할 수 있는 JavaScript이지만 여전히 테스트입니다. 이 과정의 후반부에서 다룰 도구를 포함하여 테스트를 작성하는 데 도움이 되는 많은 도구가 있지만, 모두 문제가 발생할 경우 오류를 유발한다는 기본 원칙에 따라 작동합니다.

라이브러리 테스트 실습

대부분의 라이브러리 또는 기본 제공 테스트 프레임워크는 테스트를 더 쉽게 작성할 수 있게 해주는 두 가지 주요 프리미티브, 즉 어설션과 독립적인 테스트를 정의하는 방법을 제공합니다. 이러한 내용은 다음 섹션인 어설션 및 기타 프리미티브에서 자세히 다룹니다. 그러나 개략적으로 보거나 작성하는 거의 모든 테스트는 이러한 종류의 프리미티브를 사용하게 된다는 점을 기억해야 합니다.

어설션은 결과 확인과 문제 발생 시 오류를 일으키는 결합 방법입니다. 예를 들어 assert를 도입하여 이전 테스트를 더 간결하게 만들 수 있습니다.

import { fibonacci } from "../src/math.js";
import { assert } from "a-made-up-testing-library";

assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");

독립적인 테스트를 정의하고 선택적으로 도구 모음으로 그룹화하여 이 테스트를 개선할 수 있습니다. 다음 도구 모음은 피보나치 함수와 Catalan 함수를 독립적으로 테스트합니다.

import { fibonacci, catalan } from "../src/math.js";
import { assert, test, suite } from "a-made-up-testing-library";

suite("math tests", () => {
  test("fibonacci function", () => {
    assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
    assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");
  });
  test("relationship between sequences", () => {
    const numberToCheck = 4;
    const fib = fibonacci(numberToCheck);
    const cat = catalan(numberToCheck);
    assert.isAbove(fib, cat);
  });
});

소프트웨어 테스트 맥락에서 명사로서의 테스트는 이전 예의 '시퀀스 간 관계' 테스트 사례와 같이 독립적이고 주소 지정 가능한 단일 시나리오인 테스트 사례를 나타냅니다.

개별적으로 이름이 지정된 테스트는 다음 작업을 비롯한 여러 작업에 유용합니다.

  • 시간 경과에 따른 테스트 성공 또는 실패 방식 결정.
  • 시나리오가 해결되었는지 더 쉽게 테스트할 수 있도록 버그 또는 시나리오를 이름으로 강조 표시합니다.
  • glob 필터 등을 통해 일부 테스트를 다른 테스트와 독립적으로 실행

테스트 사례를 생각할 수 있는 한 가지 방법은 단위 테스트의 '세 가지 A'인 정렬, 행동, 어설션을 사용하는 것입니다. 각 테스트 사례는 기본적으로 다음을 충족해야 합니다.

  • 일부 값 또는 상태를 정렬합니다 (단지 하드 코딩된 입력 데이터일 수 있음).
  • 메서드 호출과 같은 작업을 실행합니다.
  • 출력 값 또는 업데이트된 상태를 어설션합니다 (assert 사용).

테스트의 규모

이전 섹션의 코드 샘플은 소프트웨어의 사소한 부분을 테스트하므로 단일 파일에 중점을 두는 단위 테스트에 대해 설명합니다. 이 경우에는 단일 함수의 출력만 테스트합니다. 여러 파일, 구성요소 또는 서로 다른 상호 연결된 시스템 (때로는 네트워크 서비스 또는 외부 종속 항목의 동작과 같이 사용자가 제어할 수 없는 부분)의 코드를 고려하면 테스트 복잡성이 증가합니다. 따라서 테스트 유형의 이름은 범위 또는 배율에 따라 지정되는 경우가 많습니다.

단위 테스트와 함께 다른 테스트 유형의 예로는 구성요소 테스트, 시각적 테스트, 통합 테스트가 있습니다. 이러한 이름에는 엄격한 정의가 없으며 코드베이스에 따라 의미가 다를 수 있으므로 가이드로 사용하고 자신에게 적합한 정의를 찾아야 합니다. 예를 들어 시스템에서 테스트 중인 구성요소는 무엇인가요? React 개발자의 경우에는 말 그대로 'React 구성요소'에 매핑될 수 있지만, 다른 컨텍스트에서는 개발자에게 다른 의미를 가질 수 있습니다.

개별 테스트의 규모는 '테스트 피라미드'라고도 하는 개념 내에 배치할 수 있습니다. 테스트 피라미드는 테스트 대상과 실행 방법에 관한 좋은 경험 규칙이 될 수 있습니다.

테스트 피라미드는 상단에 엔드 투 엔드 (E2E) 테스트, 중간에 통합 테스트, 하단에 단위 테스트가 있습니다.
테스트 피라미드

이 아이디어는 반복되었으며 이제 테스트용 다이아몬드나 시험용 아이스콘과 같은 다양한 다른 모양이 유명화되었습니다. 테스트 작성 우선순위는 코드베이스별로 고유할 수 있습니다. 그러나 일반적인 기능은 단위 테스트와 같이 간단한 테스트는 더 빠르게 실행하고, 더 쉽게 작성하며, 제한된 범위를 테스트하는 경향이 있는 반면, 엔드 투 엔드 테스트와 같은 복잡한 테스트는 작성하기 어렵지만 더 넓은 범위를 테스트할 수 있다는 점입니다. 실제로 많은 테스트 '도형'의 최상위 레이어는 수동 테스트인 경향이 있습니다. 일부 사용자 상호작용이 자동 테스트로 코드화하기 너무 복잡하기 때문입니다.

이러한 유형은 자동 테스트 유형에서 확장됩니다.

이해도 테스트

대부분의 테스트 라이브러리와 프레임워크는 어떤 프리미티브를 제공하나요?

클라우드 제공업체를 사용하는 실행자 서비스입니다.
일부 브라우저 기반 실행기는 테스트를 아웃소싱하는 방법을 제공하지만, 이는 테스트 라이브러리의 일반적인 기능은 아닙니다.
충족되지 않을 경우 예외를 발생시키는 어설션
테스트를 통과하지 못하면 오류를 발생시킬 수 있지만 assert() 및 변형이 포함되는 경향이 있습니다. 검사를 쉽게 작성할 수 있도록 해주기 때문입니다.
테스트를 테스트 피라미드로 분류하는 방법입니다.
실제로는 표준화된 방법은 없습니다. 테스트 이름의 접두사를 붙이거나 다른 파일에 배치할 수 있지만 대부분의 테스트 프레임워크에는 분류가 적용되지 않습니다.
함수별로 독립적인 테스트를 정의하는 기능
test() 메서드는 거의 모든 테스트 실행기에 포함되어 있습니다. 테스트 코드가 파일의 최상위 수준에서 실행되지 않으므로 테스트 실행기가 각 테스트 사례를 독립적인 단위로 취급할 수 있기 때문에 중요합니다.