Software Engineering – Software Testing

Bomin Seo·2022년 7월 28일
0

Program Testing

software testing시 사용되는 data

  • 인위적으로 만들어진 data
  • 대체하고자 하는 이전의 system이 다루었던 과거의 data
    • 실제 system이 동작하면서 발생하는 data를 이용한 testing은 제일 마지막 testing단계에서 일부 사용자들의 사용을 통하여 진행된다.
  • Testing를 함으로써 error를 발견 및 수정하고, 비정상적 상황에 대한 예외 상황, non-functional한 기능의 비정상적인 동작에 대해서도 탐지한다.
  • Error의 존재를 밝힐 수 있지만, error가 없다는 것을 밝힐 수는 없다.

Program testing의 목적

1. 개발자와 customer가 설정한 요구사항을 만족하는지 확인한다. (Validation testing)

  • 테스트 대상이 명쾌하기 때문에 입력 값과 출력 값이 명쾌한 작업
  • ‘요구 사항을 만족하는가’ / ‘정상적인 입력 시에 의도된 동작을 하는가’ 에 대하여 검증한다.
  • 정상시 입력에 대하여 예측된 정상적 출력이 나오는지 검증한다.
  • 성공적인 test는 시스템이 의도한 대로 동작함을 나타낸다.

For custom software

  • 요구사항 명세에 기재된 모든 요구 사항에 대하여 최소한 1개 이상의 test를 진행하여야 한다.

For generic software products

  • 모든 system features에 대해서, feature들이 결합된 복잡한 case, 출시할 때 통합되는 features에 대하여 모두 testing이 이루어져야 한다.

2. 출시 또는 사용 전에, 요구사항에 위배되는 잘못된 동작 혹은 기존에 생각하지 못했던 잘못된 동작에 대해 탐지하고 수정 및 대응한다. (Defect testing)

  • 시스템 장애 및 충돌, 의도하지 않은 다른 시스템과의 상호작용, 잘못된 계산, 데이터 오염 등
  • 정상 입력에 잘못된 동작을 하는 경우, 비정상 입력에 정상 출력 값이 나오는 경우
  • 잘못된 입력에 대응하지 못하거나 잘못된 출력은 도출하는 경우
  • 예상하지 못한 경우에서 문제를 야기하는 경우
  • 개발자의 협업 상에서 다른 개발 환경으로 인한 문제 등
  • 생각지도 못한 비정상적인 상황을 testing하기 위하여 창의적인 생각이 필요하다.

Verification vs Validation

Verification

  • Are we building the product right : 프로그램을 specification에 맞게 만들었는지
  • 입력에 대하여 정상적인 출력을 도출하는지에 대하여 검증 (개발의 관점)

Validation (개발, 설계, 마케팅, 기획, 디자이너 등의 관점에서 유저의 입장을 고려한다)

  • Are we building the right product : 고객의 요구사항에 맞는 소프트웨어를 만들었는지
  • 유저가 원하는, 돈을 낼 만한 제품인지 검증 (유저의 관점에서, 운영의 관점에서 검증)

V & V Confidence

  • V & V의 목적은 시스템이 목적에 부합한다는 Confidence를 만드는 것

Software purpose

  • 신뢰성, 기능성 등 software specification에 부합하는지에 따라 confidence level이 결정된다.

User expectations

  • 사용자는 초기의 소프트웨어에 대한 낮은 기대감을 가지고 있다. (의심의 관점)
    • 사용자가 의심하지 않을 만큼 기능과 사용자의 관점에 대하여 검증하여야 한다.

Marketing environment

  • 시장의 상황에 따라 defect를 발견하는 것보다 빠르게 출시하는 것이 중요할 수도 있다.
    • 출시 후 보완하는 방법을 사용함에 따라 사용자의 기대감이 하락하는 원인이 된다.
  • Defect testing은 창의적인 case와 베타 테스터의 참여 등 시간이 오래 걸리기 때문에 시장의 상황에 따라 defect testing의 시간이 줄어들 수 있다.

Inspections and testing

  • Inspections와 testing은 상호보완적으로 동작하며, V & V 프로세스에서 두 방법 모두 반드시 수행 되어야한다.

Software inspections (성능, 코드의 구조, 재사용 가능 여부 등을 머리로 판단한다)

  • 코드를 분석하여 문제점을 발견한다. (static verification / code review)
  • 프로그램 동작이 필요 없기 때문에 개발 중이더라도 검증이 가능하다.
  • 요구사항, 시스템 구조, 디자인, test data 등 전 영역에 관해 적용이 가능하다.
  • testing중에는 error가 다른 연결된 프로그램의 error에 가려질 수 있지만, inspection은 정적인 process이기 때문에 다른 error와는 별개로 탐지할 수 있다.
  • 개발이 완료되지 않은, 다른 프로세스와 연결이 필요하거나 대규모의 프로젝트 중 일부만 완성되었더라도 추가적인 비용 필요 없이 부분적으로 검증이 가능하다.
  • 표준을 준수하였는지, 호환성이 좋은지, 유지성/재사용에 좋은지 검증할 수 있다.
  • Inspection의 단점
    • 명세서에 부합하는 지에 대해서는 검토할 수 있지만, 실제 고객의 요구사항에 부합하는지는 검증할 수 없으며, 프로그램의 성능과 usability에 대해서도 검증할 수 없다.

Software testing

  • 프로그램을 실행하면서 동작을 확인하는 과정 (dynamic verification)
  • 동작하는 프로그램에 입력(test data)을 주고 출력을 확인하는 과정

Development testing

  • 개발팀이 개발 중에 testing하여 bug와 defect를 발견한다.
  • 개발팀 본인이 만든 시스템을 포함하여 모든 항목에 대하여 테스트를 진행한다.

Unit testing (개발팀, Development testing 1단계)

  • 가장 작은 단위인 함수나 객체(object class with method)들에 대한 성능을 testing한다.
  • 각각의 individual component에 대해 testing하며 defect testing의 프로세스이다.
  • 클래스들을 묶는 등 unit의 조합에 따른 interface나 기능에 대해서도 검증한다.
  • 입력에 대해서 제대로 결과가 나오는지, 비정상적인 상황에 대한 반응 등을 testing한다.

Object class testing

  • Object의 모든 method들에 대해서 검증하며, object가 관리하는 state, data, attribute에 대해서 제대로 세팅하고 modifying하는 것이 가능한가 대해서 검증한다.
  • 유전의 법칙이 적용된 클래스를 검증할 때, base class 개발자의 의도에 맞지 않게 사용하면 에러를 유발할 수 있다.
  • 명세서에 기술한 상세 시나리오 별로 test case를 만들어 testing하는 것이 유용하며, state model을 사용한다면 상태 변화 sequence를 식별하고 test모델을 개발하여 testing한다.
  • 시나리오에 정의된 상태 변화에 맞추어 테스트를 하기 위하여 test프로그램을 구현한다.

Automated testing

  • TEST는 별도의 프로그램을 작성하며, Test 대상이 되는 프로그램보다 크며, 처음부터 구현하기 보다는 테스트를 write하고 run하는데 도움을 주는 framework(Junit 등)의 도움을 받을 수 있다.
  • Unit testing은 짜여진 시나리오에 맞게 자동적으로 사람의 개입 없이 함수를 호출하며 testing되도록 하여야 한다.
  • Automated unit testing에서는 test automation framework(Junit)을 사용할 수 있다.
  • framework에는 일반적으로 도움이 되는 기능(reporting, GUI 등)을 제공한다.

Automated test components

  • a setup part : 입력과 예상된 출력은 가진 test case를 가지고 시스템을 설정한다.
  • A call part : 검증될 객체나 메소드를 호출하는 단계
  • An assertion part : 호출된 결과와 예상된 결과를 비교하는 단계

Choosing unit test cases

  • Test case가 의도된 대로 사용될 때 testing하는 component가 무엇을 하는지 나타낸다.
  • Component에 defect가 있다면 test case에서 드러나야 한다.
  • 정상 입력 시에 예상된 동작을 하는 테스트 케이스를 만든다.
  • 비정상적 입력 등 문제를 야기하는 입력을 만들어 프로그램이 죽지 않는지, 제대로 처리되는지, component와 충돌하는지에 대하여 테스트할 수 있도록 해야 한다.

Testing strategies

Partition testing

  • 입력값 중에서 공통적인 특성을 가지며 같은 과정으로 처리되야 되는 입력 값으로 분할하여 test를 진행한다.

  • 데이터의 class들은 equivalence partition이거나 같은 방식으로 동작하는 class 멤버들의 partition으로 분류되며, test case는 반드시 각 partition마다 추출되어야 한다.

  • 군을 잘못 분류하면 error를 탐지하지 못하는 문제점이 발생한다.

  • 군의 경계선에 있는 데이터, 분류 기준이 바뀌는 지점의 데이터들, 데이터의 중간값, 하드웨어의 가장 작은 수와 큰 수 등으로 샘플로 선택하여 주로 검증한다.

Guideline-based testing

  • 많이 나타나는 error들은 정형화되어 있으며, 따라서 guideline에 서술된 test case에 대해서 필수적으로 검증하는 방법

General testing guidelines

  • 강제적으로 오류를 발생시키는 입력값을 선택해야 한다.
  • 입력 버퍼에 overflow가 일어나도록 하는 입력값을 넣어라.
  • 같은 입력값을 반복적으로 넣어 결과값을 확인하라
  • 강제적으로 잘못된 출력 값이 나오도록 하라
  • 계산 결과가 굉장히 크거나 작게 도출되도록 강제하라. (C와 같이 타입별 제한이 있는 경우)

Component testing (개발팀 또는 검증 전담팀, Development testing 2단계)

  • Unit의 복합체(함수가 함수를 호출하거나 객체가 다른 객체, method, 파일, 메모리 등을 호출하는 경우)의 component interface가 제대로 이루어졌는지 testing한다.
  • 상호 연관성이 큰 unit이 결합하여 component를 구성하고 이것에 대한 기능과 결합 성능 등을 검증한다.
  • Component 간의 통신의 interface를 testing하는 것이 주목적이다.

Interface testing (외부에서 test case를 보내어 내부의 interface가 동작하는지 검증한다)

  • Interface error나 interface에 대한 잘못된 가정을 인해 발생하는 fault를 탐지하는 것이 목적이다.

Interface type

  • Parameter interface : data를 하나의 method/procedure에서 다른 것으로 전달할 때 사용
    • testing : 정상 입력시 정상 동작을 하는지, 비정상 입력시 어떤 반응을 하는지 검증한다.
  • Shared memory interfaces : 함수나 procedures간의 공유 메모리 사용에 대해 검증한다.
    • Testing : 메모리를 통한 정보 교환이 잘 되는가에 대하여 검증한다.
  • Procedural interfaces : 여러 절차(보이지 않을 수도 있는)를 묶어 호출함으로써 testing한다.
    • Testing : 절차가 문제없이 동작하는지에 대하여 검증한다.
  • Message passing interfaces : 서로 다른 시스템(컴퓨터)에 있는 프로세스 간의 통신 testing
    • Testing : 통신을 통하여 메시지를 제대로 전달하는 지에 대하여 검증한다.

Interface errors

  • Interface misuse : 데이터 타입이나 데이터 순서를 잘못 준 경우
  • Interface misunderstanding : interface 사용 방법에 대해 잘못 이해한 경우
    • 데이터의 요구 사항을 제대로 지키지 않고 전달하거나 동작에 대해 이해하지 않은 경우
  • Timing errors : 협업하는 프로세스 사이에서 시간이나 속도가 맞지 않는 경우
    • 정보를 주고 받을 때 정확한 타이밍에 정확한 정보를 통신을 해야 한다.
    • shared memory/message passing에 자주 일어나는 경우
      - ex) writing된 값을 읽어온 후 작업을 하는 경우에 입력된 값을 가져오지 못한 경우
      - ex) 2개의 프로세스가 같은 값을 가지고 움직여야 하지만, 시간 값이 맞지 않아 어긋난 경우

Interface testing guidelines

  • 파라미터를 전달할 때 최대, 최소, 중간 값을 전달하거나 군으로 분류된다면 군의 최대, 최소, 중간 값을 전달할 수 있다.
  • 주소 값을 전달하는 경우 null pointer를 전달한다.
  • 잘못된 입력을 전달하여 testing하여 안정성을 test한다.
  • 일을 많이 시키는 stress testing을 실행한다(message passing 통신 프로그램과 같은 경우)
    • 트래픽이 많을 경우 일부 데이터는 처리하고 일부 데이터는 버리면서 프로그램이 죽지 않고 실행 가능한 상태여야 한다.
  • Shared memory system의 경우 components의 순서를 바꾸어 전달한다.
    • lock이나 flag를 사용하여 방지할 수 있다.

System testing (검증 전담팀 or operation 팀, Development testing 3단계)

  • components들이 integrated/build된 시스템을 testing한다.
  • components간의 상호작용을 testing한다. (system단위의 component testing)
  • component가 양립 가능한지, 서로 올바르게 상호 작용하는지, interface를 통하여 정확한 데이터를 정확한 타이밍에 전송하는지에 대하여 testing한다.
  • 독자적으로 개발된 reusable component와 외부에서 구입한 off-the-shelf system을 통합한 전체 시스템에 대해서 검증한다.
  • 다른 부서 혹은 하위 팀들이 개발한 components를 통합한 후 system level에서 테스트한다.

Use case testing

  • 각각의 use case에 대하여 순서, 절차, 기대 값에 맞게 동작하여 상호작용이 일어나게 한다.
  • 각각의 use case는 여러 개의 시스템이 동반되는 경우를 호출하며 따라서 시스템 간의 상호작용을 테스트할 수 있다.
  • Use case에 상응하는 Sequence diagrams을 통하여 component를 검증한다.
  • 정상적인 상황과 비정상적인 상황 모두에 대한 다양한 경우를 나타내야 한다.

Test case derived from sequence diagram

  • Request를 보낸 측에서는 반드시 acknowledgement를 받아야 하고, 제대로 된 acknowledge를 받았는지 검증하기 위해서 정확한 결과에 대한 데이터와 잘못된 결과에 대한 예측 결과에 대한 데이터를 request를 보낸 측에서 가지고 있어야 한다.

Release testing

  • 주요 목적은 supplier에게 충분히 사용되기에 좋은 시스템이라는 것을 납득시키기 위함이다.
  • 유저에게 전달될 완성된 버전의 software를 독자적인 testing team이 검증한다.
  • 고객의 관점에서 내부의 기능을 모르고 입/출력 만을 검증하는 black-box testing
  • System specification을 만족하는지 검증한다.
  • 버그가 발생하는 지에 대한 defect testing과 외부에서 사용하기에 적합한지 검증하는 validation testing으로 이루어진다
  • System requirement에서 정의한 요구사항을 기반으로 입력 데이터를 만든다.

Performance testing

  • Release testing의 일부분으로써, 중요한 특징인 성능이나 안정성 등에 대해서 검증한다.
  • Performance test에서는 일반적으로 load를 점진적으로 증가시키는 일련의 테스트를 거치며 시스템이 unacceptable할 때까지 진행하여 동작이 잘 되는지를 검증하며, unacceptable을 넘겼을 때는 죽지 않고, 일부를 버리면서 동작될 수 있는지 검증한다.

User testing

  • 고객이나 잠재 고객이 본인들의 환경에서 testing하며 input data나 system testing에 관한 조언을 주는 단계이며, 시스템에 대한 비용을 지불하는가에 대한 여부도 결정되는 단계이다.
  • 시스템의 종류, 앞서 어떤 테스트 과정을 거쳤는가에 관계 없이 반드시 수행되어야 한다.

Alpha testing

  • 시스템에 대하여 어느정도 지식이 있는 고객을 선별하여 개발자의 site에서 기능을 검증한다.

Beta testing

  • 기존의 소프트웨어에 어느정도 익숙한 더 큰 그룹의 고객에 대하여 검증을 진행한다.

Acceptance testing

  • 외부 고객이 시스템을 받아들여 본인의 환경에서 실행하고 대가를 지불할지에 대해 결정하며 내부 고객의 경우 결재에 대한 과정이 필요하다.

Agile methods and acceptance testing

  • Agile의 경우 user/customer의 역할을 개발팀에서 맡게 되고 acceptability에 대한 결정도 개발팀에 역할에 있다.
  • User/customer가 개발과 밀결합되어 있기 때문에 수정 사항이 발생하면 자동적으로 테스트될 수 있으며, 큰 규모의 그룹인 경우에는 개발팀과 운영팀이 별개로 존재하여 운영팀에서 acceptance에 대해서 결정할 수 있다.
  • Agile의 대표인 google의 같은 경우에도 일반 사용자를 대상으로 acceptance testing을 진행한다고 볼 수 있으며, 어떤 방식의 시스템이라도 acceptance testing은 존재한다.

Testing policy

  • 철저한 검사는 불가능하므로 test coverage를 정해야 한다.
  • 버그가 유발되는 입력이 들어오는 경우도 검증해야 한다.
  • Ex) menu에 관한 모든 시스템은 검사해야 한다.
  • Ex) 같은 메뉴 시스템에 연결된 시스템은 연결되어 검증되어야 한다.

Test-driven development (TDD, 테스트가 주도하는 개발, 주로 Agile)

  • 아무것도 구현하지 않은 상태에서 테스트 프로그램을 실행하여 fail을 발생시킨 후 fail을 해결하기 위한 코드를 구현함으로써 프로그램 작성을 시작한다.
  • 테스트 대상이 되는 프로그램을 짠 후 테스트 코드를 통해 검증한다.
  • 테스트 코드를 통과할 수 있을 만큼만의 코드만을 짠다.
  • 개발은 incrementally하게 진행되며, 각 단계별로 test code를 통과할 때까지 단계를 반복한다.

Benefits

Code coverage

  • 모든 code의 조각에 대해 test하고 구현함으로써 모든 코드에 대해 최소한 한 번 이상의 검증을 진행할 수 있다.

Regression testing

  • incrementally하게 개발이 진행되면서 앞서 검증된 코드를 다시 검증할 수 있다.
  • 새롭게 짠 코드가 기존의 코드의 동작을 영향을 주는지 테스트할 수 있다.
  • 자동화된 테스트에서는 간단하게 진행할 수 있다.

Simplified debugging

  • test가 실패했을 때 어디서 문제가 발생했는지 쉽게 알 수 있다.

System documentation

  • code가 정형화되면서 test 프로그램 자체가 일종의 문서로 사용될 수 있다.
profile
KHU, SWCON

0개의 댓글