소프트웨어 공학/Theorem

[소프트웨어 공학] 08. Software Testing

lumana 2024. 5. 9. 20:09

Software Testing

 

Program Testing(프로그램 테스트)

  • 테스팅은 프로그램이 의도한 대로 작동하는지(a program does what it is intended to do), 사용 전에 프로그램 결함을 발견(to discover program defect)하기 위해 수행됩니다.
  • 소프트웨어를 테스트할 때, artificial data(인공 데이터)를 사용하여 프로그램을 실행합니다.
  • 테스트 결과를 검토하여 오류, 이상 현상 또는 프로그램의 비기능적 속성에 대한 정보를 확인합니다.
  • 오류의 존재만을 나타내며(the presence of error), 그 부재를 나타내지는 않습니다.(Not their absence)
  • 테스팅은 정적 검증 기법도 포함하는 보다 일반적인 검증 및 확인 과정의 일부입니다.

Program testing goals(프로그램 테스트 목표)

  • 개발자와 고객에게 소프트웨어가 요구 사항을 충족함을 보여줍니다.
    • 맞춤형 소프트웨어의 경우, 요구 사항 문서의 모든 요구 사항에 대해 적어도 하나의 테스트(at least one test for every requirement)가 있어야 합니다.
    • 일반 소프트웨어 제품의 경우, 시스템 기능의 모든 테스트와 이러한 기능의 조합(tests for all of the system features, plus combinations of these features) 이 제품 출시에 포함되어야 합니다.
  • 소프트웨어의 동작이 잘못되었거나 명세에 맞지 않는 상황을 발견합니다.
  • Validating Testing(검증 테스팅)
    • 개발자와 시스템 고객에게 소프트웨어가 요구 사항을 충족한다는 것을 보여줍니다.
    • 성공적인 테스팅은 의도한 대로 시스템이 작동하는 것(the system operates as intented)을 보여준다
  • Defect Testing(결함 테스팅)
    • 소프트웨어의 동작이 잘못되었거나, 명세와 일치하지 않는 소프트웨어의 결함을 발견합니다.
    • 성공적인 테스팅은 시스템이 잘못 동작하도록 하여 시스템의 결함을 노출시키는 것이다
  • 성공적인 테스트는 시스템이 의도한 대로 작동함을 보여줍니다.

An input-output model of program testing(프로그램 테스트의 입력-출력 모델)

  • 입력 테스트 데이터에서 시스템을 통해 출력 테스트 결과로 이어집니다.
  • 입력이 이상 동작을 일으킨다면, 출력은 결함의 존재를 드러냅니다.

Verification vs validation

  • Verification:
    • "우리가 제품을 올바르게 구축하고 있는가?"
    • 소프트웨어는 명세에 반드시 부합해야 한다
  • validation: "우리가 올바른 제품을 구축하고 있는가?"
    • 소프트웨어는 사용자의 실제 요구 사항을 충족해야 합니다.

Inspections and testing(검사 및 테스트)

  • Software inspections(소프트웨어 검사)는 정적 시스템 표현을 분석하여 문제를 발견하는 데 중점을 둡니다.(static verification)
    • tool-based document와 코드 분석을 통해서 수행된다
  • Software testing(소프트웨어 테스트)는 테스트 데이터로 시스템을 실행하고 동작을 관찰하는 데 중점을 둡니다.(dynamic verification)
    • 따라서 Software testing 의 경우 테스트 케이스 작성이 필요함
    • Software testing은 NFR 체크가 가능하다
  • 둘 다 IDE에서 많이 제공해준다
    • 코드 분석같은 소프트웨어 검사 / 실행 시간 분석, 디버깅과 같은 소프트웨어 테스트
  • Inspection and testing are complementary(검사와 테스팅은 보완적이며), verification에 어긋나지 않으며, V & V 과정 중 사용되어야 합니다.
  • Inspection(검사)는 명세에 대한 conformance(일치, 준수)를 확인할 수 있지만 고객의 실제 요구 사항 준수는 확인할 수 없습니다.
  • 검사는 성능, 사용성 등의 비기능적 특성을 확인할 수 없습니다.(Inspections cannot check non-fuctional characteristics)

Stages of testing(테스팅 단계)

  • 개발 중에 시스템을 테스트하여 버그와 결함을 발견하는 개발 테스팅(Development testing).
    • 제대로 코드를 작성했는지 test case를 가지고 testing
  • 사용자에게 출시되기 전에 시스템의 완전한 버전을 테스트하는 출시 테스팅(Release testing).
    • 유저에게 제공되기 전에 이 릴리즈에 문제가 없는지 체크
  • 사용자 또는 잠재 사용자가 자신의 환경에서 시스템을 테스트하는 사용자 테스팅(User testing).
    • 유저가 통제된 환경에서 실제 테스트를 해보는 것
    • ex) 게임 beta test

Development testing(개발 테스팅)

  • Development testing은 팀이 시스템 개발 중 수행하는 모든 테스팅 활동을 포함합니다.
  • 테스팅 활동은 테스트되는 구성 요소 수준에 따라 분류될 수 있습니다.
  • Unit testing은 개별 프로그램 단위 또는 객체 클래스를 테스트합니다.
    • Unit testing은 객체나 메서드의 기능성을 테스트하는데에 중점을 둬야 한다
  • Component testing(컴포넌트 테스팅)은 여러 개별 단위를 통합하여 복합 구성 요소를 생성합니다.
    • 따라서 이 단계에서는 Interface쪽 테스트를 많이 함
    • 보통 Unit이 모아지면 Component가 구성되고, 결국엔 Component, Unit testing이 System testing을 형성하게 됨
  • System testing은 시스템 일부 또는 전체 구성요소가 통합되어 시스템 전체가 테스트되는 테스트입니다.
    • System testing은 component interaction을 테스트하는 것에 중점을 둬야 한다

Unit testing

  • Unit testing은 개별 구성 요소를 독립적으로 테스트하는 과정입니다.
  • 결함 테스트 과정(defect testing process)입니다.
  • 테스트 단위는 다음과 같을 수 있습니다:
    • 객체 내의 개별 함수 또는 메서드(Individual functions or methods within an object)
    • 여러 속성 및 메서드를 가진 객체 클래스(Object classes with several attributes and methods)
      • 대부분 framework이 Individual --> Object 테스트에 집중되어 있음
    • 정의된 인터페이스를 사용하여 기능에 접근하는 복합 구성 요소(Composite components with defined interfaces used to access their functionality)

Object class testing(객체 클래스 테스트)

  • 클래스의 완전한 테스트 범위는 다음을 포함한다
    • 객체와 관련된 모든 operations을 테스트
      • --> 모든 메서드에 대해서 테스트가 필요하다
      • ex) Inspection 과정에서 특정 private 메서드가 사용되지 않는지 체크
    • 모든 객체 속성을 설정하고 및 조회하는 것
    • 가능한 모든 상태에서 객체를 운용하는 것
      • 위 두 개는 작성하기 쉬운데, 요거는 작성하는데 난이도가 있음
      • ex) View에 라이트모드와 다크 모드가 존재한다면, 테스트 해야 할 state가 2개 --> 테스트 해야하는 상황이 많아짐
  • 상속은 테스트 정보가 지역화되지 않기 때문에 객체 클래스 테스트 설계를 더 어렵게 만듭니다.

Automated testing(자동화된 테스트)

  • Test Framework이 반복되는 테스트에 대한 기능을 지원해줌
  • 가능한 경우, 단위 테스트는 수동 개입 없이 자동화되어 실행 및 확인되어야 합니다.
  • 자동화된 단위 테스트에서는 테스트 자동화 프레임워크(예: JUnit)를 사용하여 프로그램 테스트를 작성하고 실행합니다.
  • 단위 테스트 프레임워크는 특정 테스트 사례를 생성하기 위해 확장할 수 있는 일반적인 테스트 클래스를 제공합니다.
    • 이를 통해 구현한 모든 테스트를 실행하고, GUI를 통해 테스트의 성공 여부를 보고할 수 있습니다.

Automated test components(자동화된 테스트 구성 요소)

  • 초기화 부분(setup)에서 시스템을 테스트 케이스, 즉 입력 및 예상 출력과 함께 초기화합니다.
    • test input : 파라미터를 넘긴다던지, 클래스의 인스턴스를 만들어서 method invoke를 하는 등등
  • 호출(call) 부분에서 테스트할 객체 또는 메서드를 호출합니다.
    • test input 을 던져서 call -> output
  • assertion 부분에서 호출의 결과를 예상 결과와 비교합니다.
    • output이 expected result와 일치하는지
    • 테스트가 참으로 평가되면 성공한 것이고, 거짓이면 실패한 것입니다.
  • tear-down(in korean : 해체 단계)부분에서는 리소스 해제 또는 임시 파일 정리와 같이 시스템 상태를 원래 상태로 복원합니다.
  • 처음 두 part에서는 자동화된 테스트 연구에 더욱 주의를 기울인다

Types of Unit Test Cases(단위 테스트 사례 유형)

  • 테스트 사례는 예상대로 사용될 때 테스트하는 구성 요소가 의도한 대로 작동함을 보여줘야 합니다.
  • 구성 요소에 결함이 있는 경우(defects in the component), 이는 테스트 사례에 의해 드러나야 합니다.
  • 이는 두 가지 유형의 단위 테스트 사례로 이어집니다:
    • 첫 번째 유형은 프로그램의 정상 작동을 반영(reflect normal operation of a program)하고 구성 요소가 예상대로 작동함을 보여야 합니다(show that the component works as expected).
    • 다른 유형의 테스트 사례는 일반적인 문제가 발생하는 경험을 기반으로 한다
      • 비정상적인 입력을 사용하여 이러한 입력이 적절하게 처리되고 구성 요소를 충돌시키지 않는지 확인해야 합니다.(user abnormal inputs to check that these are properly processed and do not crash the component)
      • ex) 값이 NULL로 나오거나, A / B에서 B == 0일 때 문제 없이 작동하는지?

Testing strategies(테스팅 전략)

  • Partitioning testing(분할 테스트): 입력 그룹을 식별하고, 같은 방식으로 처리되어야 하는 공통 특성을 가집니다.
    • 각 그룹 내에서 테스트를 선택해야 한다.
    • 파티션을 나눠서 대표적인 값을 뽑고, 모든 파티션에 대해서 테스트 될 수 있도록 한다
  • (Guideline-based testing)가이드라인 기반 테스팅: 테스트 가이드라인을 사용하여 테스트 사례를 선택합니다.
    • 이러한 가이드라인은 구성 요소를 개발할 때 프로그래머가 자주 저지르는 오류를 반영합니다.
    • "이런 코드에 대한 이런 실수를 많이 하더라"를 바탕으로 체크하는 test case를 만든다.

Partition testing(분할 테스트)

  • 입력 데이터와 출력 결과는 종종 클래스의 모든 멤버가 서로 연관되어 있는 다른 클래스로 나뉩니다.
  • 각 클래스는 동등한 분할 또는 도메인으로, 프로그램이 각 클래스의 모든 멤버에 대해 동등하게 작동하는 곳입니다.
  • 각 분할에서 테스트 사례를 선택해야 합니다.
  • ex) int n의 input으로 올 수 있는 것 --> (0, 자연수, 음수) or (0 이상, 음수)
  • ex) 4 / 2 나 2 / 3의 경우에 대한 output group

Equivalence partitioning(동등 분할)

 

 

 

Testing guidelines(collections) / 테스팅 가이드라인 (컬렉션)

  • 단일 값만 포함하는 collections을 사용하여 소프트웨어를 테스트합니다.
    • 예) List list = new ArrayList(); list.add(0);
  • 다양한 크기의 컬렉션을 사용하여 다른 테스트를 수행합니다.
  • 첫 번째, 중간, 마지막 요소가 접근되도록 테스트를 구성합니다.
    • ex) index 0, list.size() - 1
  • 길이가 0인 컬렉션으로 테스트합니다.

General testing guidelines(일반 테스팅 가이드라인)

  • 시스템이 모든 오류 메시지를 생성하도록 입력을 선택합니다.
    • 예) Java try/catch. 모든 catch 블록을 고려합니다.
  • 입력 버퍼가 오버플로우되도록 입력을 설계합니다.
    • ex) int 형의 범위, floating point decision
  • 같은 입력이나 일련의 입력을 여러 번 반복합니다.
  • 유효하지 않은 출력이 생성되도록 강제합니다.
  • 계산 결과가 너무 크거나 작아지도록 강제합니다.
    • ex) int a * b 에서, a==2^16, b==2^16이면 int형의 범위를 넘는다
    • ex) DB 접속 : Connection 후 close 할 때 Try-catch에서 닫아줘야 함(finally절 사용), 이에 대한 Test case 작성

Component testing(컴포넌트 테스팅)

  • 소프트웨어 컴포넌트는 종종 여러 상호 작용하는 객체로 구성된 복합 컴포넌트입니다.
    • 예를 들어, 소프트웨어는 사용자 데이터를 읽고 데이터를 분석할 수 있는 별도의 부분을 가질 수 있으며, 다양한 출처에서 얻은 데이터를 동일한 기술로 분석할 수 있습니다.
  • 정의된 컴포넌트 인터페이스를 통해 이러한 객체의 기능에 접근합니다.
    • 메서드가 다른 메서드를 사용할 때 Interface 규약이 존재함(자바에서는 javdoc)
    • ex) Input parameter를 올바르게 넘기고 있는지 Check
      • div (a, b) -> a / b 가 아니라 b / a를 리턴하도록 코드를 작성했을 수도 있음
  • 복합 컴포넌트 테스팅은 컴포넌트 인터페이스가 그 명세에 따라 동작함을 보여줘야 합니다.
    • 컴포넌트 내 개별 객체에 대한 unit tests가 완료되었다고 가정할 수 있습니다.

Interface testing(인터페이스 테스팅)

  • 목표는 인터페이스 오류나 인터페이스에 대한 잘못된 가정으로 인한 결함을 탐지하는 것입니다.
  • 프로세스가 여러 개이거나, 멀티 쓰레드 환경에서 주로 발생하는 것들을 check
  • 인터페이스 유형:
    • Parameter interfaces(매개변수 인터페이스): 한 메서드나 프로시저에서 다른 것으로 데이터가 전달됩니다.
    • Shared memory interfaces(공유 메모리 인터페이스): 메모리 블록이 프로시저나 함수 간에 공유됩니다.
    • Procedural interfaces(절차 인터페이스): 하위 시스템이 다른 하위 시스템에 의해 호출될 일련의 절차를 캡슐화합니다.
      • ex) Deadlock 테스트
    • Message passing interfaces(메시지 전달 인터페이스): 하위 시스템이 다른 하위 시스템으로부터 서비스를 요청합니다.

Interface errors(인터페이스 오류)

  • Interface misuse(인터페이스 오용)
    • 호출 컴포넌트가 다른 컴포넌트를 호출하고 그 인터페이스 사용에서 오류를 범합니다.
    • 예) 매개변수 순서가 잘못되었습니다.
  • Interface misunderstanding
    • 호출 컴포넌트가 호출된 컴포넌트의 동작에 대한 잘못된 가정을 포함합니다.
    • 예) null 대신 비어 있거나 기본 객체를 반환합니다.
  • Timing errors(타이밍 오류)
    • 호출된 컴포넌트와 호출 컴포넌트가 다른 속도로 작동하고 시간 정보에 접근이 이루어질 때 오류가 발생합니다.
    • ex) Shared resource에 대한 raise condition

Interface testing guidelines(인터페이스 테스트 가이드라인)

  • 호출된 프로시저에 대한 매개변수가 그 범위의 극단적인 끝에 있을 때 테스트를 설계하세요.
    • 코너 케이스나 경계 케이스입니다.
  • 항상 null 포인터를 가진 포인터 매개변수를 테스트하세요.
  • 컴포넌트가 실패하도록 테스트를 설계하세요.
  • 메시지 전달 시스템에서 스트레스 테스트를 사용하세요.
  • 공유 메모리 시스템에서는 컴포넌트가 활성화되는 순서를 변경하세요.

System testing(시스템 테스트)

  • 개발 중 시스템 테스트는 컴포넌트를 통합하여 시스템 버전을 만든 다음 통합된 시스템을 테스트하는 것을 포함합니다.
  • 시스템 테스트의 초점은 컴포넌트 간의 상호작용을 테스트하는 것입니다(testing the interactions between components).
    • Component 테스트를 위해 testcode를 짜게 되는데, 시스템을 전부 돌려보는 등으로 체크함
  • 시스템 테스트는 컴포넌트가 올바르게 상호작용하고 인터페이스를 통해 적절한 시간에 올바른 데이터를 전송하는지 확인합니다.
  • 시스템 테스트는 시스템의 긴급 동작(the emergent behaviour of a system)을 테스트합니다.

System and component testing(시스템 및 컴포넌트 테스트)

  • 시스템 테스트 중에는 별도로 개발된 재사용 가능한 컴포넌트와 시판용(off-the-shelf) 시스템이 새로 개발된 컴포넌트와 통합될 수 있습니다.
    • 그런 다음 전체 시스템이 테스트됩니다.
  • 다른 팀 멤버 또는 하위 팀에 의해 개발된 컴포넌트는 이 단계에서 통합될 수 있습니다.
    • 여태까지 개발한 테스트를 모아서 System testing이 되는 것에 가까움 
    • 시스템 테스트는 개별적인 프로세스가 아닌 집단적인 프로세스입니다.
    • 일부 회사에서는 설계자 및 프로그래머의 참여 없이 별도의 시스템 테스트 팀이 관여할 수 있습니다.
      • ex) 회사 QA 팀에서 Component를 모아서 System testing을 한다

Use-case testing(유스케이스 테스트)

  • 시스템 상호작용을 식별하기 위해 개발된 use-cases는 시스템 테스트의 기초로 사용될 수 있습니다.
  • 각 use case는 일반적으로 여러 시스템 컴포넌트를 포함하므로 테스트 중에 이러한 상호작용이 발생하도록 합니다.
  • use case 에 연관된 순서 다이어그램은 테스트되고 있는 구성요소 및 상호작용을 설명할 수 있습니다.
  • ex) UML, 복잡한 코드의 메서드 호출 순서(A->B->C...)

Testing Policies(테스트 정책)

  • 철저한 시스템 테스트는 불가능하므로 필요한 시스템 테스트 커버리지를 정의할 수 있는 테스팅 정책이 개발될 수 있습니다.
    • 이 정도까지 테스트 하면 괜찮다 를 정의
  • 테스팅 정책 예시:
    • 메뉴를 통해 접근되는 모든 시스템 기능을 테스트해야 합니다.
    • 같은 메뉴를 통해 접근되는 기능 조합(예: 텍스트 포맷팅)을 테스트해야 합니다.
      • 각 기능을 조합했을 때 문제가 없는지 check
    • 사용자 입력이 제공되는 경우, 모든 기능은 정확한 입력과 잘못된 입력 모두에서 테스트되어야 합니다.
    • 원자로 시스템 같은 경우에는 더 엄격한 Testing Policy를 가짐

Test-driven Development

  • Test-driven development(테스트 주도 개발(TDD)은 테스트와 코드 개발을 교차 진행하는 프로그램 개발 방식입니다.
  • 테스트는 코드 작성 전에 작성되며, 테스트 통과가 개발의 주요 동인입니다.
  • Setup, call, assertion 단계 중에서 Setup에서 어려움을 주로 겪음
  • 코드를 점진적으로 개발하며, 그 단계에 맞는 테스트를 함께 개발합니다.
    • 다음 단계로 넘어가기 전에 개발된 코드가 그 테스트를 통과해야 합니다.
  • TDD는 익스트림 프로그래밍과 같은 애자일 방법의 일부로 소개되었습니다.
    • 하지만, 계획 주도 개발 과정에서도 사용될 수 있습니다.

Benefits of Test-driven Development

  • Code coverage(코드 커버리지)
    • 작성하는 모든 코드 세그먼트에는 적어도 하나의 테스트가 연결되어 모든 코드가 테스트를 받습니다.
    • 내가 작성한 코드에 대해서 test case를 가지고 있기 때문에, code Coverage가 올라감
  • Regression testing(회귀 테스트)
    • 새로운 코드를 작성해도 기존 코드가 잘 작동하는지 
    • 프로그램이 개발됨에 따라 점진적으로 회귀 테스트 모음(suite)이 개발됩니다.
  • Simplified debugging(디버깅 간소화)
    • 왜 오류가 발생하는지 하나씩 실행을 해야하는데, test를 돌려봄으로써 어떤 부분에 문제가 있는지 체크할 수 있고, 이 부분을 자동화 할 수 있음 
    • 테스트 실패 시 문제가 있는 위치가 명확해지며, 새로 작성된 코드를 확인하고 수정해야 합니다.
  • System documentation(시스템 문서화)
    • Agile에서는 documentation을 최소화해야하는데, test case가 Specification과 같은 역할을 해준다 
    • 테스트 자체가 코드가 수행해야 할 작업을 설명하는 문서 형태로 작용합니다.

Regression Testing

  • Regression Testing(회귀 테스트)는 시스템을 테스트하여 변경 사항이 이전에 작동하던 코드를 '깨트리지' 않았는지 확인합니다.
    • ex) method X를 수정할 때, X를 호출하는 다른 메서드에서 문제가 발생하지 않는지 확인한다
  • 수동 테스팅 과정에서 회귀 테스트는 비용이 많이 들지만, 자동화 테스팅을 사용할 경우 간단하고 직관적입니다.
    • 변경이 이루어질 때마다 모든 테스트를 다시 실행합니다.
  • 테스트는 변경 사항이 커밋되기 전에 '성공적으로' 수행되어야 합니다.
  • TDD는 회귀 테스트를 확보하는 좋은 방법입니다.

Code Coverage

  • Statement Coverage(or Line Coverage) / 구문 커버리지(또는 라인 커버리지)
    • statement가 실행되면 충족됩니다.
    • 일반적으로 Code Coverage라고 함은 Statement Coverage를 말함
  • Branch Coverage(or Decision Coverage)
    • 각 branch(분기)가 실행되면 충족됩니다.
    • 코드 상에서 Branch가 발생하는 경우 (실행 경로가 달라지는 경우)에 실행할 수 있는 서로 다른 경로가 잘 실행되는지
      • ex) if-else 문에서 실행 경로가 갈리짐
  • Condition Coverage(조건 커버리지)
    • branch(분기) 내 각 조건이 테스트되면 충족됩니다.
    • ex) (A && B) || C 에서 A, B, C의 T/F

Increasing Code Coverage

  • 테스트 케이스의 setup 부분과 call 부분을 신중하게 설계함으로써 코드 커버리지를 높일 수 있습니다.
  • 자바에서는 private 메소드가 public 메소드에 의해 호출되어야 합니다.
    • 테스트가 모든 public 메소드를 호출하고, 메소드 내의 모든 분기를 취하면 이론적으로 100% 라인/분기 커버리지를 달성할 수 있습니다.
    • 만약 private 메서드를 호출 안한다면 그냥 그 메서드를 지우고, 필요할 때 commit log를 확인해서 가져다 쓰자
  • 이를 위해 적절한 객체 인스턴스를 생성하는 것이 중요하며, 이는 메소드를 호출하는 데 사용될 수 있습니다.
    • 좋은 모듈화는 프로그램의 각 단위에 대한 테스트 케이스를 작성하기 쉽게 만듭니다.
  • Tip
    • 메서드가 100줄이라고 하면, Unit test에서는 input을 던져서 100줄 다 Cover 할 수 있도록 하자
    • 이를 위해서 if() A, else B라고 하면 A와 B를 다른 메서드로 분리해줄 수 있다(Clean-Code에 있는 내용인 듯)
    • java에서는 mockito의 Mock-up을 이용해서 커버리지를 올릴 수 있다
  • 테스트 스윗의 라인 커버리지가 99%라고 하더라도, 에러를 더 잘 발견한다고 보기 어렵다. 적절한 Input을 구성해야 함

 

Limitation of Code Coverage

  • 코드 커버리지는 테스트 스위트의 품질 또는 성능을 평가하는 데 사용됩니다.
    • 코드의 얼마나 많은 부분이 테스트 되었나요?
  • 자동화 테스트 연구는 종종 분기 커버리지를 사용합니다.
    • RandooP, EvoSuite 같은 도구가 JUnit 테스트 케이스 생성에 사용됩니다.
    • 물론, 이렇게 해서 커버리지를 올려도 실제로 오류 발견 확률이 올라가지 않을 수 있음
  • 최근 연구에 따르면 코드 커버리지만으로는 테스트 스위트 평가에 충분하지 않습니다.
    • 실제로 결함을 감지하는지에 대한 질문이 제기됩니다.
  • Mutation testing(돌연변이 테스트): 코드를 자동으로 변형하여 결함을 심고, 테스트가 결함을 감지할 수 있는지 확인합니다.
    • Java 돌연변이 테스트를 위한 PIT 도구가 사용됩니다.
    • 코드 일부를 변형한다면(ex. If문을 제거하거나, a = b와 같은 statement 제거) 에러가 발생해야 하는데, 이를 체크한다

 

TDD Example

 

 

 

 

 

 

// NFR test(계산 속도)
@Test
@Timeout(value = 1, unit = TimeUnit.SECONDS)
void testFibonnaci2() {
	assertTimeout(Duration.ofMillis(1), () —> {
		SimpleMath.fibonacci(50);
	});
}

// 메서드 구현 코드

public static int fibonacci(int n) {
	return fibonacci(n-1, 1, 1);
}

private static int fibonacci(int n, int pp, int p) {
	if (n <= 2) {
		return p;
	} else {
		return fibonacci(n-1, p, pp*p);
	}
}

 

 

Release Testing

  • 릴리즈 테스팅은 개발 팀 외부에서 사용하도록 의도된 특정 릴리즈의 시스템을 테스트(testing a particular release of a system that is intended for use outside of the development team)하는 과정입니다.
  • 릴리즈 테스팅의 주요 목표는 시스템 공급자에게 시스템이 사용에 충분히 좋다는 것을 확신시키는 것입니다.
    • 따라서, 릴리즈 테스팅은 시스템이 지정된 기능, 성능 및 신뢰성을 제공하며 정상 사용 중에 실패하지 않음을 보여야 합니다.
  • 릴리즈 테스팅은 일반적으로 블랙박스 테스팅 과정으로, 테스트는 시스템 명세에서만 파생됩니다.
    • 화이트박스 테스팅과 대조됩니다.

Release testing and system testing(릴리스 테스팅과 시스템 테스팅)

  • 릴리스 테스팅은 시스템 테스팅의 한 형태입니다.
  • 중요한 차이점:
    • 시스템 개발에 참여하지 않은 별도의 팀이 릴리스 테스팅을 담당해야 합니다.
    • 개발 팀에 의한 시스템 테스팅은 시스템 내 버그를 발견하는 데 중점을 둬야 합니다 (결함 테스팅) / focus on discovering bus in the system(defct testing).
    • 릴리스 테스팅의 목적은 시스템이 요구 사항을 충족하고 외부 사용에 충분히 좋은지 확인하는 것입니다 (검증 테스팅). / to check that the system meets its requirements and is good enough for external use(validation testing)

Requirements based testing(요구 사항 기반 테스팅)

  • 요구 사항 기반 테스팅(Requirements-based testing)은 각 요구 사항을 검토하고 이에 대한 테스트를 개발하는 것을 포함합니다.
  • 이미 중간 릴리스 목록에 대한 요구 사항 기반 테스팅 경험이 있습니다.
  • 개발된 테스트가 반드시 자동화된 테스트일 필요는 없습니다.
    • 단순히 시나리오를 정의하여 요구 사항이 충족되는지 확인할 수 있습니다.

Performance testing(성능 테스팅)

  • 릴리스 테스트의 일부에는 성능 및 안정성과 같은 시스템의 새로운 속성 테스트가 포함될 수 있습니다.
  • 테스트는 시스템 사용 프로필을 반영해야 합니다.
  • 성능 테스트는 일반적으로 시스템 성능이 수용할 수 없을 때까지 부하를 점진적으로 늘리는 일련의 테스트를 계획하는 것을 포함합니다.
  • 스트레스 테스트는 시스템을 고의로 과부하 상태로 만들어 그 실패 행동을 테스트하는 성능 테스트의 한 형태입니다.
  • ex) API request가 10000개 일 때, 5초 안에 return 해줘야 한다
  • 보통 Development 팀이 아니라 외부 team(ex. QA team)이 이런 것들을 test 함

User testing(사용자 테스팅)

  • 사용자 또는 고객 테스팅은 사용자 또는 고객이 시스템 테스팅에 대한 의견을 제공하는 테스트 과정의 단계입니다.
  • 사용자 테스팅은 포괄적인 시스템 및 릴리스 테스팅이 수행된 후에도 필수적입니다.
    • 이는 사용자의 작업 환경에서 발생하는 영향(influnces from the user's working environment have a major effect)이 시스템의 신뢰성, 사용성 및 견고성에 주요한 영향을 미치기 때문입니다.
      • target env랑, working env를 고려해야 한다.
      • ex) MAC의 Javadoc과 Linux의 Javadoc 차이 존재
    • 이러한 것들은 테스트 환경에서 재현할 수 없습니다.

Types of user testing(사용자 테스팅의 유형)

  • Alpha testing(알파 테스팅)
    • 소프트웨어의 사용자가 개발자 사이트에서 개발 팀과 함께 소프트웨어를 테스트합니다.
    • ex) 유저를 초청해서 Test하거나, 회사 내부에서 테스트를 진행하는 등
  • Beta testing(베타 테스팅)
    • 소프트웨어의 릴리스가 사용자에게 제공되어 시스템과 관련된 문제를 발견하고 제기할 수 있도록 합니다.
    • 불특정 다수를 대상으로 Release를 배포하여 feedback을 받는다
  • Acceptance testing(승인 테스팅)
    • 고객이 시스템을 테스트하여 시스템 개발자로부터 배포되고 고객 환경에서 사용할 준비가 되었는지를 결정합니다. 주로 맞춤형 시스템에 사용됩니다.

 

User Testing for Different Types of Software(다양한 소프트웨어 유형에 대한 사용자 테스팅)

  • 소프트웨어가 사용자 테스팅에 충분히 개발되었음에도 불구하고 아직 테스팅 단계에 있습니다.
  • 이 기간 동안 발견되지 않은 오류가 발생할 수 있습니다.
  • 따라서 다양한 소프트웨어 유형에 대해 사용자 테스팅을 설계하고 수행할 필요가 있습니다.
    • 예를 들어, 온라인 게임의 베타 테스팅은 자율 주행 소프트웨어나 금융 소프트웨어에 비해 더 쉽게 수행될 수 있습니다.

Agile methos and acceptance testing(애자일 방법론 및 승인 테스팅)

  • 애자일 방법에서 사용자/고객은 개발 팀의 일부이며 시스템의 수용성에 대한 결정을 내리는 책임이 있습니다.
  • 테스트는 사용자/고객에 의해 정의되며 변경이 이루어질 때 자동으로 실행되도록 다른 테스트와 통합됩니다.
  • 여기서 주요 문제는 내장된 사용자가 '전형적'이고 모든 시스템 이해관계자의 이익을 대표할 수 있는지 여부입니다.
  • Acceptance testing(ex. 계약맺고 개발하여 제공하고, 돈을 정산 받는 구조) --> Agile은 이런 과정이 아님

요약

  • Program testing은 verification 및 validation 과정에서 중요한 부분입니다.
    • 테스팅의 두 가지 주요 목표는 요구 사항 확인과 결함 발견입니다.
  • Software inspection는 문제를 발견하기 위한 static verification이며, software testing은 프로그램 동작을 관찰하기 위한 dynamic verification입니다.
  • 프로그램 테스팅은 development testing, release testing and user testing으로 구성됩니다.
  • TDD는 테스트와 코드 개발을 교차하여 진행하는 접근 방식으로, 개발된 프로그램이 통과해야 하는 테스트 케이스 작성부터 시작합니다.
  • 유닛테스트 / Partition Test는 꼭 기억하자