2015년 4월 6일 월요일

Unit Test Junit, TDD, and CI(수업 노트01TDD)

서두

완벽한 요구사항이란 가능한가?
===> No (본인이 구매자라도 힘들지 않을까?)

그렇다면 불안전하며 언제 바뀔지 모르는 요구사항에 대응하는 시도는?
===> 대부분의 요구사항의 변경에 대한 확장성을 지니는 프로그램 개발
여기서는 개발 방식 (개발 모델) 관점에서 언급해보면

개발 방법론

CMMi :
폭포수 개발 모델 :
개발 순서 : 요구사항 -> 명세화 -> 코딩(구현) -> 테스팅 -> 배포
쿨하긴 한데 단간 명료하고 이 방식에 살을 좀 붙이면
현재에서 쓰일수 있다.

Agile/XP :
폭포수 개발 모델을 80H 단위로 실행가능한 상태를 나누어서
점차적으로 개발
기능의 일부를 구현하고 보여줄수 있어야 한다는 점이 핵심

V&V 모델
(이걸 개발모델이라고 할수 있다면)
그중 구현 단계에서 진행할수 있는 테스트 툴이 Junit
(기능테스트, 비기능, 구조, 확정 및 회귀 테스트(수정에 대한 영향도))
여기에서는 시스템 테스트에 대해서 부하 테스트, 보안 테스트에
비중이 큰걸 지적함 등등 여러가지 가 있다.

본론으로 들어가서 테스트와 디버깅의 차이
★ ①테스트 : 오류를 발견하기 위한 방법 (예방 주사)
★ ②디버깅 : 이미 발견된 오류의 원인을 진단하고 수정하는 방법
(치료약 처방 또는 수술)



사례1) 하나의 수정후 (특히 웹 프로젝트에서 자바 부분이 바뀐는 경우)
서버를 재부팅하는 과정을 계속 하고 테스트 확인을 하는 과정이
반복되면 --> 개발자는 지겹게 느껴진다.

사례2) 또 전체의 일부분을 담당할때 다른 부분이 완성되지 않았을때
테스트를 할수 없는 상황이 된다.

test 에서 Unit(단위)란 : 테스트 가능한 최소한의 소프트웨어 요소
test 프레임워크 종류 :

Xunit(Cunit, DBUnit, 등 오픈소스)
Junit
Dbunit
Easymock
Tmock
Unitils
.......
단위 테스트를 넘어서 의존성 관계를 테스트 위해 Maven을 많이
사용한다고 하더라...
---------------------------------------------------개념잡기 끝

○UnitTest의 시작점인 TDD 개념 알아보기

TDD(Test Driven Development) (프로그램 작성전 테스트를 먼저 작성)
(1) 테스트코드를 먼저 작성하여 실패를 보고(에러를 확인하여)
(2) 그 실패를 보완하는 과정에서 실제 구현 코드를 작성함

이렇게 하면 테스트 내용이 보완된 내용이 작성됨(자체 보완되면서)
복잡해지면 중복된 내용을 발견하고 리팩토링이 요구되고
이 요구되는 리팩토링에 준하여 작성해가면 우리가 원하는
정확하게 구동되는 프로그램을 얻을수 있을 것이다.

------------- 밥 아저씨(Robert C, Martin) 의 TDD 원칙

1. 실패하는 테스트를 작성하기 전에는 절대로 제품 코드를
작성하지 않는다.
2. 실패하는 테스트 코드를 한번에 하나 이상 작성하지 ㅇ낳는다.
3. 현재 실패하고 있는 테스트를 통과하기에 충분한 정도를 넘어서는 제품
코드를 작성하지 않는다.
----------------------------------------------------

잘 동작하는 깔끔한 코드를 위해 개발

XP(Extreme-Programming‎)의 실천 방식 중 한가지 임
(ask -> Respond -> Refine -> Repeat)중복제거(repectoring)
--> 나중에 TDD에서 Junit 개념으로 발전하게 됨

단점!!!!

but 나중에 보면 자신이 만든 테스트 안에서 만족하는 코드를
작성하면 완벽?
현실에 적용하는데에 결정할 사항이 많고 본인이
필요한 기능만이 나오기 때문에
완벽한 테스트 케이스가 좀더 세부적이고 요구사항에 정확하게
근접해야 그에 따라
해당 사항들이 보완되는 프로그램이 나올수 있다.



개념 이해를 위한 예제ex)

요구사항 : 1계좌번호를 생성(생성시 잔금이 10000만원인 계좌가 생성된다)
2. 잔고를 조회

구현

1. testcase 작성
AccountTest라는 클래스를 만들고 여기에는 계좌가 생성된다.
아직 Account.java가 생성되지 않았기 때문에 빨간줄이 뜰듯.

1
2
3
4
5
6
7
8
public class AccountTest {
 public void testAccount() throws Exception{
  Account account = new Account(10000);
  if(account== null){
   throw new Exception("Create Account Fail");
  }
 }
}




2. 다음 다른 클래스를 만든다.
(애는 실제 사용할 클래스)

1
public class Account {}


3. AccountTest.java에 다음과 같이 메인함수를 추가한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class AccountTest {
 public void testAccount() throws Exception{
  Account account = new Account(10000);
  if(account== null){
   throw new Exception("Create Account Fail");
  }
 }
 public static void main(String[] args) {
  AccountTest test = new AccountTest();
  try {
   test.testAccount();//Create Account
  } catch (Exception e) {
   System.out.println("test fail");
   return;
  }
  System.out.println("success");
 }
}
----> 에러 발생한다. why?

메인이 파라미터가 결과적으로 파라미터가 들어간
생성자를 호출하게되는데
기본 생성자가 아니니 해당 구문 실행시 에러 발생

4. 에러 발생을 확인했으니 다시 Account를 수정

1
2
3
4
5
6
7
package Sample2;
 public class Account {
  public Account(int i) {
  return =10000;
 }
}
------>에러는 나지 않지만 이상하다 왜?
Account 생성자에서 파라미터 숫자를 몇을 받던 10000 이라는
숫자를 반환하는데 10000이라는 돈이 없으면 통장을 못만드는
상황이라는 설명을 들었다.

------>메인에서 input 값을 여러개 넣으면 초기값 10000원 빼면 다
틀린값이 반환된다고 봐야 된다.
빨간줄은 없지만 원하는 프로그램이 아니므로 에러라고 판단
하고 이를 수정하도록 한다.

5. 빨간줄은 없지만 원하는 프로그램이 아니므로 에러가
있다고 판단하고 이를 수정하기 위해 Account.java를
수정하기로 한다.

1
2
3
4
5
6
7
8
9
public class Account {
 static int money =0;
 public Account(int i) {
  this.money=i;
 }
 public int getBalance() {
  return money;
 }
}

------>입력한 값이 그대로 입력되는 형태로 변환되어 생성시 입력한 값이
그대로 적용되도록 변경되었다.



이런 형태로 진행하는 방식이 TDD 방식인데
중요한건 기본 형태의 프로그램을 작성하고 에러를 잡고,
원하는 형태의 프로그램으로 맞춰가는 과정이라고 보면 되겠다.
(에러를 확인 --> 에러를 고치기 위해 작성 --> 테스트)

---------------------------------------------------

단점 : 테스트를 작성하기 전에 요구사항을 보고 먼저 테스트를
도출하기 위한 두뇌회전을 함으로서
테스트를 유도한 상태이다.

테스트를 위한 방향을 제대로 잡지 않고 100%만을 추구하면
테스트소스가 길어지고 길어지고 길어져서 배보다 배꼽이 커진다.
이런 점 때문에 TDD 는 논란이 있다.


(이날 완성 소스)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package Sample2;
public class AccountTest {
 public void testAccount() throws Exception{
  Account account = new Account(10000)
  if(account== null){
   throw new Exception("Create Account Fail");
  }
 }
 public void testGetBalance() throws Exception{
  Account account = new Account(10000);
  if(account.getBalance() != 10000){
   throw new Exception("now equal balance gold value");
  }
  account = new Account(1000);
  if(account.getBalance() != 1000){
   throw new Exception("now equal balance gold value");
  }
  account = new Account(100);
  if(account.getBalance() != 100){
   throw new Exception("now equal balance gold value");
  }
 }
 public void testDeposit() throws Exception{
  Account account = new Account(10000);
  account.deposit(1000);
  if(account.getBalance() !=11000){
   throw new Exception("now equal balance gold value your value is :"+account.getBalance());
  } 
 }
 public void testWithdraw() throws Exception{
  Account account = new Account(10000);
  account.withdraw(2000);
  if(account.getBalance() !=8000){
   throw new Exception("now equal balance gold value your value is : "+account.getBalance());
  }
 }
 public static void main(String[] args) {
  AccountTest test = new AccountTest();
  try {
   test.testAccount();//Create Account
   test.testGetBalance();//view gold balance
   test.testDeposit();//input money
   test.testWithdraw();
   } catch (Exception e) {
   e.printStackTrace();
  System.out.println("test fail");
  return;
 }
 System.out.println("success");
 }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package Sample2;
public class Account {
 static int money =0;
 public Account(int i) {
  this.money=i;
 }
 public int getBalance() {
  return money;
 }
 public int deposit(int i){
  this.money += i;
  return money;
 }
 public int withdraw(int i) {
  this.money -= i;
  return money;
 }
}




댓글 없음:

댓글 쓰기