본문 바로가기

Java

[Java] Interface. - 아주 간단한 테스트 프레임웍 만들어보기 2 -

앞선 포스트에서 테스트 프로그램을 만들어서 돌리는 간단한 프로그램을 만들었습니다.
물론 내부 로직은 다 빠지고 형태만 보여주기 위한 코드였고요..

최종적으로 나온 Tester.java 의 모습은 아래와 같았습니다.

package testfw;
/**
* @author need4spd, need4spd@cplanet.co.kr, 2010. 5. 19.
*
*/
public class Tester {
public static void main(String[] args) {
ProductListsCompareTester tester = new ProductListsCompareTester();
boolean isPassed = tester.isPassed();
System.out.println("통과여부 : " + isPassed);
ProductRankScoreTester tester2 = new ProductRankScoreTester();
isPassed = tester2.isPassed();
System.out.println("통과여부 : " + isPassed);
}
}
view raw 1.java hosted with ❤ by GitHub


2개의 테스터 클래스가 보입니다. 내부로직으로 구현되어 있지는 않지만
ProductListsComapreTester.java는 상품 리스트를 검색해서 그 정렬 순서가 원하는 순서대로
정렬되어 있는지 확인하는 Tester 클래스입니다.

ProductRankScoreTester.java는 다른 두개의 검색 서버가 있다고 가정 할 때 각각의 서버에 검색을 하고
그 두개의 결과가 일치하는지 확인하는 Tester 클래스입니다. 물론, 내부 로직은 구현을 하지 않았습니다.

일단, 앞에서 이제 인터페이스를 사용한다고 하였는데
Tester.java를 보시면 어떻게 인터페이스를 만들면 좋을지 대충 짐작도 되실겁니다.

바로 만들어서 그 인터페이스를 사용해보겠습니다.


ProductTester.java
package testfw;
public interface ProductTester {
boolean isPassed();
}
view raw 2.java hosted with ❤ by GitHub

그리고, 이를 구현하도록 위 두개의 테스터 클래스를 수정해보겠습니다.


ProductRankScoreTester.java
package testfw;
import java.util.List;
public class ProductRankScoreTester implements ProductTester {
private List<Product> searchResult;
private boolean isPassed = true;
public ProductRankScoreTester() {
initTestData();
}
private void initTestData() {
//검색을 해서List<Product>에 셋팅
System.out.println("ProductRankScoreTester : 검색 후 SET");
}
@Override
public boolean isPassed() {
System.out.println("ProductRankScoreTester 테스트 수행");
//테스트 수행 후 isPassed에 결과 setting
return isPassed;
}
}
view raw 3.java hosted with ❤ by GitHub
ProductListsCompareTester.java
package testfw;
import java.util.List;
public class ProductListsCompareTester implements ProductTester {
private List<Product> firstSearchResult;
private List<Product> secondSearchResult;
private boolean isPassed = true;
public ProductListsCompareTester() {
initTestData();
}
private void initTestData() {
//검색을 해서List<Product>에 셋팅
System.out.println("ProductListsCompareTester : 첫번째 리스트 검색 후 SET");
System.out.println("ProductListsCompareTester : 두번째 리스트 검색 후 SET");
}
public boolean isPassed() {
System.out.println("ProductListsCompareTester 테스트 수행");
//테스트 수행 후 isPassed에 결과 setting
return isPassed;
}
}
view raw 4.java hosted with ❤ by GitHub

이미 isPassed라는 이름의 메서드를 공통적으로 가지고 있었기 때문에 쉽게 수정이 가능합니다. 그럼,
Tester.java는 어떻게 바뀔까요?


Tester.java
package testfw;
public class Tester {
public static void main(String[] args) {
ProductTester tester = new ProductListsCompareTester();
boolean isPassed = tester.isPassed();
System.out.println("통과여부 : " + isPassed);
ProductTester tester2 = new ProductRankScoreTester();
isPassed = tester2.isPassed();
System.out.println("통과여부 : " + isPassed);
}
}
view raw 5.java hosted with ❤ by GitHub

각 구현체 클래스를 사용 해 참조 변수를 선언하던 부분이 인터페이스를 사용하여 선언하는 것으로 수정되었습니다.
이 상태로 후배에게 "어떤 이점이 있을 것 같아?" 라고 얘기했더니 tester라는 참조 변수를 재사용 할 수 있다. 라고 대답
하더군요...

ProductTester tester = new ProductListsCompareTester();
boolean isPassed = tester.isPassed();
tester = new ProductRankScoreTester();
isPassed = tester2.isPassed();

"재사용하면 뭐가 좋은데?" 라고 하니
"메모리... " 우물쭈물 대답하더군요...

재사용하는 것은 객체여야하지 변수를 재사용하는 것은 이득이 없다라고 얘기 해주고.. 또, 객체도 어지간히 크거나 무거운 객체가 아니며 재사용 하는 것이 아주 눈에 띄는 성능 향상을 가져오지 않는다고 얘기해주었습니다.

개인적으로는, 변수의 재사용을 잘 하지 않는 편입니다.

아무튼.. 변수의 재사용이 중요한 것이 아니라 참조 변수를 선언하는 데 사용한 타입 (ProductTester 인터페이스)이 같다는 것이
중요한 것 입니다.

테스트를 하기 위한 로직은 ProductTester 인터페이스를 구현한 각각의 구현 클래스들이 알아서 가지고 있고,
외부에서는 isPassed라는 메서드를 통해 그 테스트 결과가 통과인지 실패인지를 알 수만 있으면 됩니다.
그것이 이번에 간단하게 만들어보려고 하는 테스트 프레임워크 (라고 이름 붙이기도 민망한..)의 핵심입니다.

그럼, 인터페이스를 사용하였으니 이를 사용해서 Tester 클래스들을 모두 가져와서
일괄적으로 테스트를 실행 할 수 있는 MultiTester를 만들어보겠습니다.


MultiTester.java
package testfw;
import java.util.ArrayList;
import java.util.List;
public class MultiTester {
private List<ProductTester> testLists = new ArrayList<ProductTester>();
private boolean isAllPassed = false;
public void addTester(ProductTester tester) {
this.testLists.add(tester);
}
public boolean isAllPassed() {
for(ProductTester tester : testLists) {
isAllPassed = tester.isPassed();
}
return isAllPassed;
}
}
view raw 6.java hosted with ❤ by GitHub

인터페이스를 사용했기 때문에 ProductTester라는 Type을 갖는 List를 사용하여 모든 Tester 클래스를 받고
이를 사용해서 테스트를 일괄적으로 수행하고 있습니다.
addTester메서드의 파라메터가 ProductTester인것과 isAllPassed 메서드의 for문에 마찬가지로 ProductTester 인터
페이스가 사용 된 것을 잘 보시고요...

그럼 이것을 어떻게 사용 할까요?


Tester.java
package testfw;
public class Tester {
public static void main(String[] args) {
MultiTester multiTester = new MultiTester();
multiTester.addTester(new ProductListsCompareTester());
multiTester.addTester(new ProductRankScoreTester());
System.out.println("통과여부 : " + multiTester.isAllPassed());
}
}
view raw 7.java hosted with ❤ by GitHub

테스터 클래스들이 같은 인터페이스를 구현하고 있기 때문에
MultiTester는 위 처럼 다른 구현 클래스를 하나의 ProductTester로써 가질 수 있게 됩니다.

테스터 클래스는 현재 사용하고 있는 Service나 Controller 혹은 DAO를 사용하여
테스트를 수행하도록 되어 있다고 가정해보면..
위 Tester를 하루에 한번씩 돌리도록 스케쥴을 지정하고 그 결과로 메일로 발송하게 되면
누군가 Service나 DAO의 로직을 잘못 수정했을 때 위 테스터에 의해서 그 결과를 바로 받을 수 있을 것 입니다.

이게 아주 초보적인 테스트 자동화라고 볼 수도 있겠죠..

만약에 서비스 클래스가 늘어나서 그 서비스 클래스를 테스트 하기 위한 테스터를 만들어
위 자동 테스터 프레임워크에 적용하고 싶다면?

간단합니다..
ProductTester 인터페이스를 구현하고, 이를 MultiTester에 add만 해주면 되는 것이죠..



NewServiceTester.java
package testfw;
/**
* @author need4spd, need4spd@cplanet.co.kr, 2010. 6. 10.
*
*/
public class NewServiceTester implements ProductTester {
@Override
public boolean isPassed() {
// 테스트를 수행합니다.
return false;
}
}
view raw 8.java hosted with ❤ by GitHub
Tester.java
package testfw;
public class Tester {
public static void main(String[] args) {
MultiTester multiTester = new MultiTester();
multiTester.addTester(new ProductListsCompareTester());
multiTester.addTester(new ProductRankScoreTester());
//새로운 테스터 추가
multiTester.addTester(new NewServiceTester());
System.out.println("통과여부 : " + multiTester.isAllPassed());
}
}
view raw 9.java hosted with ❤ by GitHub

Tester에 저렇게 추가만 하면 되지요...

그런데, 테스터가 추가 될 때마다 Tester 클래스를 수정해줘야 하는 것은 조금 귀찮을 수 있습니다.
다음에는 이것을 프로퍼티를 사용해서 클래스 수정 없이 테스터를 추가해주는 것을 보겠습니다.

reflect에 대한 이야기가 나오겠고요...

그 이후에는 Annotation을 사용해서 아예 프로퍼티 조차 수정하지 않아도
테스터가 적용 되도록 해보겠습니다....