### : 목차 구분 기호
--- : 목차 내에 항목 구분 기호
@@@ : 태그 용도
,,, : 같은 목차 내에 구분 기호
목차
1. 이론 및 정보
2. 설정 및 그 밖에
3. 소스코드 또는 실습
4. 과제
###################################
1. 이론 및 정보
-----------------------------------
* AOP(Aspect Oriented Programming) - Spring을 대표하는 기능
... 이어서
4) 구현 방법
ㄱ) 스프링에서 제공하는 객체
MethodBeforeAdvice
AfterReturningAdvice
ThrowsAdvice
MethodInterceptor
ㄴ) POJO - 일반클래스 이용
ProceedingJoinPoint
org.aspectj.aspectweaver
ㄷ) Annotation
@Aspect, @Around, ...
org.aspectj.aspectweaver
-----------------------------------
* JUnit : 단위 테스트 프레임워크
TDD : Test Driven Develop - 테스트 주도 개발 방식
소프트웨어 개발론
1. 폭포수 개발
2. Agile
- XP : agile, xp를 융합해서 많이 씀
ㄱ. pair coding 짝코딩 : 한명이 없어도 일을 개발할 수 있음
ㄴ. TDD : 테스트를 먼저함, 테스트 코드를 만드는데 JUnit을 사용
왜 TDD를 써야하나?
1. 고전적인 개발 방식에서 나타날 수 있는 문제
- 특정 모듈 개발 기간이 길어질 수록 개발자의 목표의식이 흐려진다.
- 작업 분량이 늘어날 수록 확인이 어려워진다.
- 개발자의 집중력이 필요해진다.
- 논리적인 오류를 찾기가 힘들다.
- 코드의 사용법과 변경 이력을 개발자의 기억력에 의존하게 되는 경우가 많다.
- 코드 수정시에 기존 코드의 정상 동작에 대한 보장이 어렵다.
어록? Test the program before you write it. - kent back
=> 테스트에 맞춰서 코드를 짜라
Clean code that works - Ron Jeffries
=> 테스트 하면서 코드도 리팩토링해 가는 방식
테스트 -> 코드 수정 -> 리팩토링 -> 테스트 -> ...
-----------------------------------
* TDD 예제1 시나리오 - first 패키지
메서드명 : sum
Argument : int a, int b
Return Type : int
Condition : a와 b를 더한 값을 결과로 돌려 줌
-----------------------------------
* TDD Cycle
1. 질문 - 코드가 맞는지 질문을 함
2. 응답 - 질문에 해결책
3. 정제 - 리팩토링
이 흐름을 계속 반복해 나가는 과정 TDD
-----------------------------------
* TDD 예제2 시나리오 - 실습 3-6, bank 패키지
기본 시나리오
은행 계좌 클래스
- 계좌 잔고 조회
- 입급 / 출금
- 예상 복리 이자(추가)
...
첫번째 질문 : 계좌 생성 테스트
class name : Account
function
- 잔고 조회
- 입금
- 출금
...
desc : 금액은 원 단위(예 : 천원 = 1000)
첫번째 응답 : 계좌 생성 구현
첫번째 정제 :
- 소스의 가독성이 적절한가?
- 중복된 코드는 없는가?
- 이름이 잘 못 부여된 메서드나 변수는 없는가?
- 구조의 개선이 필요한 부분은 없는가?
두번째 질문 - JUnit을 사용
- 잔고 조회 기능 작성을 위한 테스트 케이스(@Test 붙여라) 작성
- 테스트 수행 결과가 errors로 표시된 항목은 failures로 만든다.
class name : Account
function
- 잔고 조회
10000원 계좌 생성
잔고 조회 결과 일치
두번째 응답 : 잔고 조회 기능 구현
두번째 정제
- 변수명 고침 : i 변수를 money로 교체
- JUnit에서 제공하는 메서드 사용 : if문 대신 assertEquals()로 교체
세번째 질문
- 입금
10000원으로 계좌 생성
1000원 입금
잔고가 11000원 확인
- 출금
10000원으로 계좌 생성
1000원 출금
잔고가 9000원 확인
세번째 응답 : 입금과 출금 기능 구현
세번째 정제
변수명을 의미있게 수정
-----------------------------------
* 예전 개발방식의 문제점
폭포수 개발 방식에서는 절반 이상에 테스트와 디버깅 하는데 씀
TDD는 이러한 시간을 소비를 줄일 수 있음
-----------------------------------
* 하나의 테스트는 반드시 하나의 기능만 테스트를 하라!!
-----------------------------------
* JUnit 사용
버전 3, 버전 4 있는데
3버전이 오래쓰임
두 버전의 차이?
version 3 : 상속 사용, 메서드 접두사로 'test'가 들어갔음
version 4 : annotation 사용
테스트 케이스 : 테스트 할 수 있는 클래스
JUnit도 git, maven처럼 cmd 명령으로 사용할 수 있는데
이클립스에 내장되어 있음, 많이 사용하니까
내장은 되어있으나 프로젝트에 빌드path를 연결해줘야 한다
or @Test 적고 에러난 부분에서 Alert Icon을 이용해서 자동 추가
JUnit Errors의 문제는 개발자의 능력에 문제? 코드의 문제
JUnit Failures 를 많이 찾아내는게 JUnit을 가장 큰 사용 목적
JUnit에서 에러 발생 시킬때 사용하는 메서드
fail();
-----------------------------------
* TDD 장점
1) 개발의 방향을 잃지 않도록 유지해 준다.
2) 품질 높은 소프트웨어 모듈을 보유
3) 자동화된 단위 테스트를 갖게 된다.
4) 사용설명서 & 의사소통의 수단
5) 설계 개선
6) 보다 자주 성공
-----------------------------------
* JUnit의 사용법을 익혀서 이력서에 넣을 정도가 되어라
-----------------------------------
* Spring의 Mock 객체(유사한, Sample)
DB가 있는 것처럼 제공
-----------------------------------
###################################
2. 설정 및 그 밖에
-----------------------------------
* 프로젝트 생성
Maven Project
Group ID - com.study.myproject
Artifact ID - AopTest
-----------------------------------
* 패키지 생성
스프링의 도움으로 만들 패키지
/AopTest/src/main/java/myaop
스프링 도움없이 만들 패키지
/AopTest/src/main/java/mypojo
/AopTest/src/main/java/myanno
-----------------------------------
* 프로젝트 생성
Java Project
/TDDPractice
-----------------------------------
###################################
3. 소스코드 또는 실습
-----------------------------------
3-1
프로젝트 생성 및 라이브러리 설정
Workspace : ~\study\SpringWork
/AopTest/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.study.myproject</groupId>
<artifactId>AopTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>AopTest</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
-----------------------------------
3-2
/AopTest/src/main/java/myaop 패키지
AOP를 SPRING을 이용해서 구현
Workspace : ~\study\SpringWork
/AopTest/src/main/java/myaop/myaop_config.xml
Spring Bean Configuration File로 만듬
/AopTest/src/main/java/myaop/App.java
/AopTest/src/main/java/myaop/MessageBean.java
/AopTest/src/main/java/myaop/MessageBeanImpl.java
메서드 성능테스트 구현할 예정
시간을 측정하는 코드가 기존 코드에 삽입이 될 예정
그런데 이 코드들이 수십,수백개의 메서드에서 쓰이니까
코드를 분리 할텐데, 분리한 코드를 다시 불러서 쓰게 됨
또한 시간을 측정해야하는 메서드들이 지금 사용중인지
모니터링을 해야하는데 이점이 어려움
이러한 어려움을 AOP로 해결할 것임
Advice로 사용할 클래스 만듬, MethodInterceptor를 씀 Around Advice를 써야 하니까
/AopTest/src/main/java/myaop/LoggingAdvice.java
LoggingAdvice가 MessageBeanImpl의 sayHello()를 감시할 것임
포인트컷이 어디인지 알려줘야함 -> 설정 파일에서 설정을 함
/AopTest/src/main/java/myaop/myaop_config.xml
이번에 예제의 Target Class는 MessageBeanImpl
/AopTest/src/main/java/myaop/App.java
package myaop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("myaop/myaop_config.xml");
MessageBean bean = ctx.getBean("proxy", MessageBean.class);
bean.sayHello();
}
}
/AopTest/src/main/java/myaop/LoggingAdvice.java
package myaop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.StopWatch;
public class LoggingAdvice implements MethodInterceptor{
// invoke() 메서드가 두번 실행 되는 메서드
// 매개변수에 가로채고자 하는 것이 전달됨
public Object invoke(MethodInvocation arg0) throws Throwable {
StopWatch watch = new StopWatch();
String methodName = arg0.getMethod().getName();
// 시계 구별 용도를 설정하면서 시작
watch.start(methodName);
System.out.println("[LOG]Method : "+methodName+" 시작됨");
// 중지된 메서드 다시 실행, 정상진행이 되는지 결과값을 받음
// 결과값을 반드시 리턴해야 진행이 됨
Object obj = arg0.proceed();
// 중지된 메서드의 내용이 전부 실행 되고, 시간 측정
watch.stop();
System.out.println("[LOG]Method : "+methodName+" 종료");
System.out.println("[LOG]처리시간 : "+watch.getTotalTimeSeconds()+"초");
return obj;
}
}
/AopTest/src/main/java/myaop/MessageBean.java
package myaop;
public interface MessageBean {
void sayHello();
}
/AopTest/src/main/java/myaop/MessageBeanImpl.java
package myaop;
public class MessageBeanImpl implements MessageBean {
private String name;
public void setName(String name) {
this.name = name;
}
public void sayHello() {
// 시작시간을 측정하여 로그에 기록하고 화면에 출력
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("안녕하세요~~" + name + "님!");
// 끝나는시간을 측정하여 로그에 기록하고 화면에 출력
// 총 걸린 시간을 로그에 기록하고 화면출력
}
}
/AopTest/src/main/java/myaop/myaop_config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageBean" class="myaop.MessageBeanImpl">
<property name="name" value="홍길동"></property>
</bean>
<bean id="loggingAdvice" class="myaop.LoggingAdvice"></bean>
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern">
<value>.*sayHello.*</value>
</property>
</bean>
</property>
<property name="advice" ref="loggingAdvice"/>
</bean>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="messageBean"/>
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
</beans>
-----------------------------------
3-3
/AopTest/src/main/java/mypojo 패키지
에서 스프링에서 지원하지 않는 AOP 실습
Workspace : ~\study\SpringWork
다운로드 받아야함
/AopTest/pom.xml 추가
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
기존의 코드는 수정하지 않아도 됨
/AopTest/src/main/java/mypojo/MessageBean.java
/AopTest/src/main/java/mypojo/MessageBeanImpl.java
아래 파일들은 수정
/AopTest/src/main/java/mypojo/LoggingAdvice.java
/AopTest/src/main/java/mypojo/mypojo_config.xml
AOP Namespaces에서 AOP 추가
<bean id="loggingAdvice" class="myaop.LoggingAdvice"></bean>
를 지우고 어노테이션으로 함
/AopTest/src/main/java/mypojo/App.java
package mypojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("mypojo/mypojo_config.xml");
MessageBean bean = ctx.getBean("messageBean", MessageBean.class);
bean.sayHello();
}
}
/AopTest/src/main/java/mypojo/LoggingAdvice.java
package mypojo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Component
public class LoggingAdvice {
// invoke() 메서드가 두번 실행 되는 메서드
// 매개변수에 가로채고자 하는 것이 전달됨
public Object invoke(ProceedingJoinPoint arg0) throws Throwable {
StopWatch watch = new StopWatch();
String methodName = arg0.getSignature().getName();
// 시계 구별 용도를 설정하면서 시작
watch.start(methodName);
System.out.println("[LOG]Method : "+methodName+" 시작됨");
// 중지된 메서드 다시 실행, 정상진행이 되는지 결과값을 받음
// 결과값을 반드시 리턴해야 진행이 됨
Object obj = arg0.proceed();
// 중지된 메서드의 내용이 전부 실행 되고, 시간 측정
watch.stop();
System.out.println("[LOG]Method : "+methodName+" 종료");
System.out.println("[LOG]처리시간 : "+watch.getTotalTimeSeconds()+"초");
return obj;
}
}
/AopTest/src/main/java/mypojo/MessageBean.java
package mypojo;
public interface MessageBean {
void sayHello();
}
/AopTest/src/main/java/mypojo/MessageBeanImpl.java
package mypojo;
public class MessageBeanImpl implements MessageBean {
private String name;
public void setName(String name) {
this.name = name;
}
public void sayHello() {
// 시작시간을 측정하여 로그에 기록하고 화면에 출력
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("안녕하세요~~" + name + "님!");
// 끝나는시간을 측정하여 로그에 기록하고 화면에 출력
// 총 걸린 시간을 로그에 기록하고 화면출력
}
}
/AopTest/src/main/java/mypojo/mypojo_config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:component-scan base-package="mypojo"/>
<bean id="messageBean" class="mypojo.MessageBeanImpl">
<property name="name" value="임꺽정"></property>
</bean>
<aop:config>
<aop:aspect id="logging" ref="loggingAdvice">
<aop:pointcut expression="execution(* sayHello())" id="logPointcut"/>
<aop:around method="invoke" pointcut-ref="logPointcut"/>
</aop:aspect>
</aop:config>
</beans>
-----------------------------------
3-4
/AopTest/src/main/java/myanno 패키지
가장 간단하게 코드를 줄일 수 있는 AOP 실습
Workspace : ~\study\SpringWork
mypojo 패키지에서 다운 받은 라이브러리를 사용할 것임
이번 예제에서도 mypojo 처럼 수정할 것만 수정하면 됨
/AopTest/src/main/java/myanno/LoggingAdvice.java
/AopTest/src/main/java/myanno/App.java
/AopTest/src/main/java/myanno/myanno_config.xml
/AopTest/src/main/java/myanno/App.java
package myanno;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("myanno/myanno_config.xml");
MessageBean bean = ctx.getBean("messageBean", MessageBean.class);
bean.sayHello();
}
}
/AopTest/src/main/java/myanno/LoggingAdvice.java
package myanno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Component
@Aspect
public class LoggingAdvice {
// invoke() 메서드가 두번 실행 되는 메서드
// 매개변수에 가로채고자 하는 것이 전달됨
@Around("execution(* sayHello())")
public Object invoke(ProceedingJoinPoint arg0) throws Throwable {
StopWatch watch = new StopWatch();
String methodName = arg0.getSignature().getName();
// 시계 구별 용도를 설정하면서 시작
watch.start(methodName);
System.out.println("[LOG]Method : "+methodName+" 시작됨");
// 중지된 메서드 다시 실행, 정상진행이 되는지 결과값을 받음
// 결과값을 반드시 리턴해야 진행이 됨
Object obj = arg0.proceed();
// 중지된 메서드의 내용이 전부 실행 되고, 시간 측정
watch.stop();
System.out.println("[LOG]Method : "+methodName+" 종료");
System.out.println("[LOG]처리시간 : "+watch.getTotalTimeSeconds()+"초");
return obj;
}
}
/AopTest/src/main/java/myanno/MessageBean.java
package myanno;
public interface MessageBean {
void sayHello();
}
/AopTest/src/main/java/myanno/MessageBeanImpl.java
package myanno;
public class MessageBeanImpl implements MessageBean {
private String name;
public void setName(String name) {
this.name = name;
}
public void sayHello() {
// 시작시간을 측정하여 로그에 기록하고 화면에 출력
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("안녕하세요~~" + name + "님!");
// 끝나는시간을 측정하여 로그에 기록하고 화면에 출력
// 총 걸린 시간을 로그에 기록하고 화면출력
}
}
/AopTest/src/main/java/myanno/myanno_config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:component-scan base-package="myanno"/>
<bean id="messageBean" class="myanno.MessageBeanImpl">
<property name="name" value="신돌석"></property>
</bean>
<aop:aspectj-autoproxy/>
</beans>
-----------------------------------
3-5
/TDDPractice TDD 연습
/TDDPractice/first 패키지
Workspace : ~\study\SpringWork
/TDDPractice/src/first/Calculator.java
/TDDPractice/src/first/Calculator.java
package first;
public class Calculator {
public int sum(int a, int b){
return 0;
}
public static void main(String[] args) {
Calculator cal = new Calculator();
System.out.println(cal.sum(10, 20)==30);
System.out.println(cal.sum(1, 2)==3);
System.out.println(cal.sum(-10, 20)==10);
System.out.println(cal.sum(0, 0)==0);
}
}
-----------------------------------
3-6
TDD 예제2 시나리오 - 첫번째 질문
Workspace : ~\study\SpringWork
코드(main 패키지)와 테스트(test 패키지) 코드를 분리시키는게 관리에 유리
/TDDPractice/bank/main
/TDDPractice/bank/test
패키지 생성
/TDDPractice/src/bank/test/AccountTest.java
/TDDPractice/src/bank/main/Account.java
-----------------------------------
3-7
TDD 예제2 시나리오 - 두번째 질문 - JUnit
Workspace : ~\study\SpringWork
JUnit을 사용하면 main 메서드를 만들 필요 없음
/TDDPractice/src/bank/test/AccountTest.java
기존 main지우고 testAccount()에 @Test 어노테이션 붙임
에러아이콘 눌러서 Build Path에 JUnit 4 추가
실행방법
menu - run - run as - junit test
프로젝트 우클릭 - run as - junit
Green Light가 나오면 성공임
-----------------------------------
3-8
TDD 예제2 시나리오 - 세번째 질문
Workspace : ~\study\SpringWork
중복된 변수(account) 우클릭 - Quick Fix or Refactor - Covert local to Field
중복된 인스턴스 생성을 메서드로 뺀다 account
account = new Account(10000); 를 블록 잡고 우클릭
Refactor - extract method
-----------------------------------
/TDDPractice/src/bank/test/AccountTest.java
package bank.test;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import bank.main.Account;
public class AccountTest {
private Account account;
@Test
public void testAccount() throws Exception {
setup();
}
@Test
public void testGetBalance(){
setup();
assertEquals(10000, account.getBalance());
account = new Account(1000);
assertEquals(1000, account.getBalance());
account = new Account(100);
assertEquals(100, account.getBalance());
}
@Test
public void testDeposit(){
setup();
account.deposit(1000);
assertEquals(11000, account.getBalance());
}
public void setup() {
account = new Account(10000);
}
@Test
public void testWithdraw(){
setup();
account.widthdraw(1000);
assertEquals(9000, account.getBalance());
}
}
// 3-8 정제
//package bank.test;
//
//import static org.junit.Assert.assertEquals;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// private Account account;
// @Test
// public void testAccount() throws Exception {
// setup();
// }
// @Test
// public void testGetBalance(){
// setup();
// assertEquals(10000, account.getBalance());
//
// account = new Account(1000);
// assertEquals(1000, account.getBalance());
//
// account = new Account(100);
// assertEquals(100, account.getBalance());
// }
// @Test
// public void testDeposit(){
// setup();
// account.deposit(1000);
// assertEquals(11000, account.getBalance());
// }
// public void setup() {
// account = new Account(10000);
// }
// @Test
// public void testWithdraw(){
// setup();
// account.widthdraw(1000);
// assertEquals(9000, account.getBalance());
// }
//}
// 3-8 errors, 3-8 Failures , 3-8 Success
//package bank.test;
//
//import static org.junit.Assert.assertEquals;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// assertEquals(10000, account.getBalance());
//
// account = new Account(1000);
// assertEquals(1000, account.getBalance());
//
// account = new Account(100);
// assertEquals(100, account.getBalance());
// }
// @Test
// public void testDeposit(){
// Account account = new Account(10000);
// account.deposit(1000);
// assertEquals(11000, account.getBalance());
// }
// @Test
// public void testWithdraw(){
// Account account = new Account(10000);
// account.widthdraw(1000);
// assertEquals(9000, account.getBalance());
// }
//}
// 3-7 assertEquals
//package bank.test;
//
//import static org.junit.Assert.assertEquals;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// assertEquals(10000, account.getBalance());
//
// account = new Account(1000);
// assertEquals(1000, account.getBalance());
//
// account = new Account(100);
// assertEquals(100, account.getBalance());
// }
//}
// 3-7 테스트 여러개 늘림, 3-7 여러 테스트를 만족시킴
//package bank.test;
//
//import static org.junit.Assert.fail;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// if (account.getBalance() != 10000) {
// fail();
// }
//
// account = new Account(1000);
// if (account.getBalance() != 1000) {
// fail();
// }
//
// account = new Account(100);
// if (account.getBalance() != 100) {
// fail();
// }
// }
//}
// 3-7 Failures 발생시키기, 3-7 성공
//package bank.test;
//
//import static org.junit.Assert.fail;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// if (account.getBalance() != 10000) {
// JUnit에서 에러 발생 시킬때 사용하는 메서드
// fail();
// throw new Exception();
// }
// }
//}
// 3-7 Errors 발생시키기
//package bank.test;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// if(account.getBalance() != 10000){
// throw new Exception();
// }
// }
//}
// 3-6 실습
//package bank.test;
//import bank.main.Account;
//public class AccountTest {
// public void testAccount() throws Exception {
// Account account = new Account();
// if (account == null) {
// throw new Exception("계좌 생성 실패");
// }
// }
// public static void main(String[] args) {
// AccountTest test = new AccountTest();
// try {
// test.testAccount();
// } catch (Exception e) {
// System.out.println("실패(X)");
// return;
// }
// System.out.println("성공(O)");
// }
//}
-----------------------------------
/TDDPractice/src/bank/main/Account.java
package bank.main;
public class Account {
private int balance;
public Account(int money) {
balance = money;
}
public int getBalance() {
return balance;
}
public void deposit(int money) {
balance += money;
}
public void widthdraw(int money) {
balance -= money;
}
}
// 3-8 Success, 3-8 정제
//package bank.main;
//
//public class Account {
// private int balance;
// public Account(int money) {
// balance = money;
// }
//
// public int getBalance() {
// return balance;
// }
//
// public void deposit(int money) {
// balance += money;
// }
//
// public void widthdraw(int money) {
// balance -= money;
// }
//}
// 3-8 Failures
//package bank.main;
//
//public class Account {
// private int balance;
// public Account(int money) {
// balance = money;
// }
//
// public int getBalance() {
// return balance;
// }
//
// public void deposit(int money) {
// }
//
// public void widthdraw(int money) {
// }
//}
// 3-7 여러 테스트를 만족시킴, 3-8 errors
//package bank.main;
//
//public class Account {
// private int balance;
// public Account(int i) {
// balance = i;
// }
//
// public int getBalance() {
// return balance;
// }
//}
// 3-7 성공, 3-7 테스트 여러개 늘림
//package bank.main;
//
//public class Account {
//
// public Account(int i) {
// // TODO Auto-generated constructor stub
// }
//
// public int getBalance() {
// // TODO Auto-generated method stub
// return 10000;
// }
//}
// 3-7 Failures 발생시키기
//package bank.main;
//
//public class Account {
//
// public Account(int i) {
// // TODO Auto-generated constructor stub
// }
//
// public int getBalance() {
// // TODO Auto-generated method stub
// return 0;
// }
//}
//3-6 실습, 3-7 Errors 발생시키기
//package bank.main;
//public class Account {
//}
-----------------------------------
###################################
--- : 목차 내에 항목 구분 기호
@@@ : 태그 용도
,,, : 같은 목차 내에 구분 기호
목차
1. 이론 및 정보
2. 설정 및 그 밖에
3. 소스코드 또는 실습
4. 과제
###################################
1. 이론 및 정보
-----------------------------------
* AOP(Aspect Oriented Programming) - Spring을 대표하는 기능
... 이어서
4) 구현 방법
ㄱ) 스프링에서 제공하는 객체
MethodBeforeAdvice
AfterReturningAdvice
ThrowsAdvice
MethodInterceptor
ㄴ) POJO - 일반클래스 이용
ProceedingJoinPoint
org.aspectj.aspectweaver
ㄷ) Annotation
@Aspect, @Around, ...
org.aspectj.aspectweaver
-----------------------------------
* JUnit : 단위 테스트 프레임워크
TDD : Test Driven Develop - 테스트 주도 개발 방식
소프트웨어 개발론
1. 폭포수 개발
2. Agile
- XP : agile, xp를 융합해서 많이 씀
ㄱ. pair coding 짝코딩 : 한명이 없어도 일을 개발할 수 있음
ㄴ. TDD : 테스트를 먼저함, 테스트 코드를 만드는데 JUnit을 사용
왜 TDD를 써야하나?
1. 고전적인 개발 방식에서 나타날 수 있는 문제
- 특정 모듈 개발 기간이 길어질 수록 개발자의 목표의식이 흐려진다.
- 작업 분량이 늘어날 수록 확인이 어려워진다.
- 개발자의 집중력이 필요해진다.
- 논리적인 오류를 찾기가 힘들다.
- 코드의 사용법과 변경 이력을 개발자의 기억력에 의존하게 되는 경우가 많다.
- 코드 수정시에 기존 코드의 정상 동작에 대한 보장이 어렵다.
어록? Test the program before you write it. - kent back
=> 테스트에 맞춰서 코드를 짜라
Clean code that works - Ron Jeffries
=> 테스트 하면서 코드도 리팩토링해 가는 방식
테스트 -> 코드 수정 -> 리팩토링 -> 테스트 -> ...
-----------------------------------
* TDD 예제1 시나리오 - first 패키지
메서드명 : sum
Argument : int a, int b
Return Type : int
Condition : a와 b를 더한 값을 결과로 돌려 줌
-----------------------------------
* TDD Cycle
1. 질문 - 코드가 맞는지 질문을 함
2. 응답 - 질문에 해결책
3. 정제 - 리팩토링
이 흐름을 계속 반복해 나가는 과정 TDD
-----------------------------------
* TDD 예제2 시나리오 - 실습 3-6, bank 패키지
기본 시나리오
은행 계좌 클래스
- 계좌 잔고 조회
- 입급 / 출금
- 예상 복리 이자(추가)
...
첫번째 질문 : 계좌 생성 테스트
class name : Account
function
- 잔고 조회
- 입금
- 출금
...
desc : 금액은 원 단위(예 : 천원 = 1000)
첫번째 응답 : 계좌 생성 구현
첫번째 정제 :
- 소스의 가독성이 적절한가?
- 중복된 코드는 없는가?
- 이름이 잘 못 부여된 메서드나 변수는 없는가?
- 구조의 개선이 필요한 부분은 없는가?
두번째 질문 - JUnit을 사용
- 잔고 조회 기능 작성을 위한 테스트 케이스(@Test 붙여라) 작성
- 테스트 수행 결과가 errors로 표시된 항목은 failures로 만든다.
class name : Account
function
- 잔고 조회
10000원 계좌 생성
잔고 조회 결과 일치
두번째 응답 : 잔고 조회 기능 구현
두번째 정제
- 변수명 고침 : i 변수를 money로 교체
- JUnit에서 제공하는 메서드 사용 : if문 대신 assertEquals()로 교체
세번째 질문
- 입금
10000원으로 계좌 생성
1000원 입금
잔고가 11000원 확인
- 출금
10000원으로 계좌 생성
1000원 출금
잔고가 9000원 확인
세번째 응답 : 입금과 출금 기능 구현
세번째 정제
변수명을 의미있게 수정
-----------------------------------
* 예전 개발방식의 문제점
폭포수 개발 방식에서는 절반 이상에 테스트와 디버깅 하는데 씀
TDD는 이러한 시간을 소비를 줄일 수 있음
-----------------------------------
* 하나의 테스트는 반드시 하나의 기능만 테스트를 하라!!
-----------------------------------
* JUnit 사용
버전 3, 버전 4 있는데
3버전이 오래쓰임
두 버전의 차이?
version 3 : 상속 사용, 메서드 접두사로 'test'가 들어갔음
version 4 : annotation 사용
테스트 케이스 : 테스트 할 수 있는 클래스
JUnit도 git, maven처럼 cmd 명령으로 사용할 수 있는데
이클립스에 내장되어 있음, 많이 사용하니까
내장은 되어있으나 프로젝트에 빌드path를 연결해줘야 한다
or @Test 적고 에러난 부분에서 Alert Icon을 이용해서 자동 추가
JUnit Errors의 문제는 개발자의 능력에 문제? 코드의 문제
JUnit Failures 를 많이 찾아내는게 JUnit을 가장 큰 사용 목적
JUnit에서 에러 발생 시킬때 사용하는 메서드
fail();
-----------------------------------
* TDD 장점
1) 개발의 방향을 잃지 않도록 유지해 준다.
2) 품질 높은 소프트웨어 모듈을 보유
3) 자동화된 단위 테스트를 갖게 된다.
4) 사용설명서 & 의사소통의 수단
5) 설계 개선
6) 보다 자주 성공
-----------------------------------
* JUnit의 사용법을 익혀서 이력서에 넣을 정도가 되어라
-----------------------------------
* Spring의 Mock 객체(유사한, Sample)
DB가 있는 것처럼 제공
-----------------------------------
###################################
2. 설정 및 그 밖에
-----------------------------------
* 프로젝트 생성
Maven Project
Group ID - com.study.myproject
Artifact ID - AopTest
-----------------------------------
* 패키지 생성
스프링의 도움으로 만들 패키지
/AopTest/src/main/java/myaop
스프링 도움없이 만들 패키지
/AopTest/src/main/java/mypojo
/AopTest/src/main/java/myanno
-----------------------------------
* 프로젝트 생성
Java Project
/TDDPractice
-----------------------------------
###################################
3. 소스코드 또는 실습
-----------------------------------
3-1
프로젝트 생성 및 라이브러리 설정
Workspace : ~\study\SpringWork
/AopTest/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.study.myproject</groupId>
<artifactId>AopTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>AopTest</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
-----------------------------------
3-2
/AopTest/src/main/java/myaop 패키지
AOP를 SPRING을 이용해서 구현
Workspace : ~\study\SpringWork
/AopTest/src/main/java/myaop/myaop_config.xml
Spring Bean Configuration File로 만듬
/AopTest/src/main/java/myaop/App.java
/AopTest/src/main/java/myaop/MessageBean.java
/AopTest/src/main/java/myaop/MessageBeanImpl.java
메서드 성능테스트 구현할 예정
시간을 측정하는 코드가 기존 코드에 삽입이 될 예정
그런데 이 코드들이 수십,수백개의 메서드에서 쓰이니까
코드를 분리 할텐데, 분리한 코드를 다시 불러서 쓰게 됨
또한 시간을 측정해야하는 메서드들이 지금 사용중인지
모니터링을 해야하는데 이점이 어려움
이러한 어려움을 AOP로 해결할 것임
Advice로 사용할 클래스 만듬, MethodInterceptor를 씀 Around Advice를 써야 하니까
/AopTest/src/main/java/myaop/LoggingAdvice.java
LoggingAdvice가 MessageBeanImpl의 sayHello()를 감시할 것임
포인트컷이 어디인지 알려줘야함 -> 설정 파일에서 설정을 함
/AopTest/src/main/java/myaop/myaop_config.xml
이번에 예제의 Target Class는 MessageBeanImpl
/AopTest/src/main/java/myaop/App.java
package myaop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("myaop/myaop_config.xml");
MessageBean bean = ctx.getBean("proxy", MessageBean.class);
bean.sayHello();
}
}
/AopTest/src/main/java/myaop/LoggingAdvice.java
package myaop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.StopWatch;
public class LoggingAdvice implements MethodInterceptor{
// invoke() 메서드가 두번 실행 되는 메서드
// 매개변수에 가로채고자 하는 것이 전달됨
public Object invoke(MethodInvocation arg0) throws Throwable {
StopWatch watch = new StopWatch();
String methodName = arg0.getMethod().getName();
// 시계 구별 용도를 설정하면서 시작
watch.start(methodName);
System.out.println("[LOG]Method : "+methodName+" 시작됨");
// 중지된 메서드 다시 실행, 정상진행이 되는지 결과값을 받음
// 결과값을 반드시 리턴해야 진행이 됨
Object obj = arg0.proceed();
// 중지된 메서드의 내용이 전부 실행 되고, 시간 측정
watch.stop();
System.out.println("[LOG]Method : "+methodName+" 종료");
System.out.println("[LOG]처리시간 : "+watch.getTotalTimeSeconds()+"초");
return obj;
}
}
/AopTest/src/main/java/myaop/MessageBean.java
package myaop;
public interface MessageBean {
void sayHello();
}
/AopTest/src/main/java/myaop/MessageBeanImpl.java
package myaop;
public class MessageBeanImpl implements MessageBean {
private String name;
public void setName(String name) {
this.name = name;
}
public void sayHello() {
// 시작시간을 측정하여 로그에 기록하고 화면에 출력
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("안녕하세요~~" + name + "님!");
// 끝나는시간을 측정하여 로그에 기록하고 화면에 출력
// 총 걸린 시간을 로그에 기록하고 화면출력
}
}
/AopTest/src/main/java/myaop/myaop_config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageBean" class="myaop.MessageBeanImpl">
<property name="name" value="홍길동"></property>
</bean>
<bean id="loggingAdvice" class="myaop.LoggingAdvice"></bean>
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern">
<value>.*sayHello.*</value>
</property>
</bean>
</property>
<property name="advice" ref="loggingAdvice"/>
</bean>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="messageBean"/>
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
</beans>
-----------------------------------
3-3
/AopTest/src/main/java/mypojo 패키지
에서 스프링에서 지원하지 않는 AOP 실습
Workspace : ~\study\SpringWork
다운로드 받아야함
/AopTest/pom.xml 추가
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
기존의 코드는 수정하지 않아도 됨
/AopTest/src/main/java/mypojo/MessageBean.java
/AopTest/src/main/java/mypojo/MessageBeanImpl.java
아래 파일들은 수정
/AopTest/src/main/java/mypojo/LoggingAdvice.java
/AopTest/src/main/java/mypojo/mypojo_config.xml
AOP Namespaces에서 AOP 추가
<bean id="loggingAdvice" class="myaop.LoggingAdvice"></bean>
를 지우고 어노테이션으로 함
/AopTest/src/main/java/mypojo/App.java
package mypojo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("mypojo/mypojo_config.xml");
MessageBean bean = ctx.getBean("messageBean", MessageBean.class);
bean.sayHello();
}
}
/AopTest/src/main/java/mypojo/LoggingAdvice.java
package mypojo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Component
public class LoggingAdvice {
// invoke() 메서드가 두번 실행 되는 메서드
// 매개변수에 가로채고자 하는 것이 전달됨
public Object invoke(ProceedingJoinPoint arg0) throws Throwable {
StopWatch watch = new StopWatch();
String methodName = arg0.getSignature().getName();
// 시계 구별 용도를 설정하면서 시작
watch.start(methodName);
System.out.println("[LOG]Method : "+methodName+" 시작됨");
// 중지된 메서드 다시 실행, 정상진행이 되는지 결과값을 받음
// 결과값을 반드시 리턴해야 진행이 됨
Object obj = arg0.proceed();
// 중지된 메서드의 내용이 전부 실행 되고, 시간 측정
watch.stop();
System.out.println("[LOG]Method : "+methodName+" 종료");
System.out.println("[LOG]처리시간 : "+watch.getTotalTimeSeconds()+"초");
return obj;
}
}
/AopTest/src/main/java/mypojo/MessageBean.java
package mypojo;
public interface MessageBean {
void sayHello();
}
/AopTest/src/main/java/mypojo/MessageBeanImpl.java
package mypojo;
public class MessageBeanImpl implements MessageBean {
private String name;
public void setName(String name) {
this.name = name;
}
public void sayHello() {
// 시작시간을 측정하여 로그에 기록하고 화면에 출력
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("안녕하세요~~" + name + "님!");
// 끝나는시간을 측정하여 로그에 기록하고 화면에 출력
// 총 걸린 시간을 로그에 기록하고 화면출력
}
}
/AopTest/src/main/java/mypojo/mypojo_config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:component-scan base-package="mypojo"/>
<bean id="messageBean" class="mypojo.MessageBeanImpl">
<property name="name" value="임꺽정"></property>
</bean>
<aop:config>
<aop:aspect id="logging" ref="loggingAdvice">
<aop:pointcut expression="execution(* sayHello())" id="logPointcut"/>
<aop:around method="invoke" pointcut-ref="logPointcut"/>
</aop:aspect>
</aop:config>
</beans>
-----------------------------------
3-4
/AopTest/src/main/java/myanno 패키지
가장 간단하게 코드를 줄일 수 있는 AOP 실습
Workspace : ~\study\SpringWork
mypojo 패키지에서 다운 받은 라이브러리를 사용할 것임
이번 예제에서도 mypojo 처럼 수정할 것만 수정하면 됨
/AopTest/src/main/java/myanno/LoggingAdvice.java
/AopTest/src/main/java/myanno/App.java
/AopTest/src/main/java/myanno/myanno_config.xml
/AopTest/src/main/java/myanno/App.java
package myanno;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("myanno/myanno_config.xml");
MessageBean bean = ctx.getBean("messageBean", MessageBean.class);
bean.sayHello();
}
}
/AopTest/src/main/java/myanno/LoggingAdvice.java
package myanno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
@Component
@Aspect
public class LoggingAdvice {
// invoke() 메서드가 두번 실행 되는 메서드
// 매개변수에 가로채고자 하는 것이 전달됨
@Around("execution(* sayHello())")
public Object invoke(ProceedingJoinPoint arg0) throws Throwable {
StopWatch watch = new StopWatch();
String methodName = arg0.getSignature().getName();
// 시계 구별 용도를 설정하면서 시작
watch.start(methodName);
System.out.println("[LOG]Method : "+methodName+" 시작됨");
// 중지된 메서드 다시 실행, 정상진행이 되는지 결과값을 받음
// 결과값을 반드시 리턴해야 진행이 됨
Object obj = arg0.proceed();
// 중지된 메서드의 내용이 전부 실행 되고, 시간 측정
watch.stop();
System.out.println("[LOG]Method : "+methodName+" 종료");
System.out.println("[LOG]처리시간 : "+watch.getTotalTimeSeconds()+"초");
return obj;
}
}
/AopTest/src/main/java/myanno/MessageBean.java
package myanno;
public interface MessageBean {
void sayHello();
}
/AopTest/src/main/java/myanno/MessageBeanImpl.java
package myanno;
public class MessageBeanImpl implements MessageBean {
private String name;
public void setName(String name) {
this.name = name;
}
public void sayHello() {
// 시작시간을 측정하여 로그에 기록하고 화면에 출력
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("안녕하세요~~" + name + "님!");
// 끝나는시간을 측정하여 로그에 기록하고 화면에 출력
// 총 걸린 시간을 로그에 기록하고 화면출력
}
}
/AopTest/src/main/java/myanno/myanno_config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<context:component-scan base-package="myanno"/>
<bean id="messageBean" class="myanno.MessageBeanImpl">
<property name="name" value="신돌석"></property>
</bean>
<aop:aspectj-autoproxy/>
</beans>
-----------------------------------
3-5
/TDDPractice TDD 연습
/TDDPractice/first 패키지
Workspace : ~\study\SpringWork
/TDDPractice/src/first/Calculator.java
/TDDPractice/src/first/Calculator.java
package first;
public class Calculator {
public int sum(int a, int b){
return 0;
}
public static void main(String[] args) {
Calculator cal = new Calculator();
System.out.println(cal.sum(10, 20)==30);
System.out.println(cal.sum(1, 2)==3);
System.out.println(cal.sum(-10, 20)==10);
System.out.println(cal.sum(0, 0)==0);
}
}
-----------------------------------
3-6
TDD 예제2 시나리오 - 첫번째 질문
Workspace : ~\study\SpringWork
코드(main 패키지)와 테스트(test 패키지) 코드를 분리시키는게 관리에 유리
/TDDPractice/bank/main
/TDDPractice/bank/test
패키지 생성
/TDDPractice/src/bank/test/AccountTest.java
/TDDPractice/src/bank/main/Account.java
-----------------------------------
3-7
TDD 예제2 시나리오 - 두번째 질문 - JUnit
Workspace : ~\study\SpringWork
JUnit을 사용하면 main 메서드를 만들 필요 없음
/TDDPractice/src/bank/test/AccountTest.java
기존 main지우고 testAccount()에 @Test 어노테이션 붙임
에러아이콘 눌러서 Build Path에 JUnit 4 추가
실행방법
menu - run - run as - junit test
프로젝트 우클릭 - run as - junit
Green Light가 나오면 성공임
-----------------------------------
3-8
TDD 예제2 시나리오 - 세번째 질문
Workspace : ~\study\SpringWork
중복된 변수(account) 우클릭 - Quick Fix or Refactor - Covert local to Field
중복된 인스턴스 생성을 메서드로 뺀다 account
account = new Account(10000); 를 블록 잡고 우클릭
Refactor - extract method
-----------------------------------
/TDDPractice/src/bank/test/AccountTest.java
package bank.test;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import bank.main.Account;
public class AccountTest {
private Account account;
@Test
public void testAccount() throws Exception {
setup();
}
@Test
public void testGetBalance(){
setup();
assertEquals(10000, account.getBalance());
account = new Account(1000);
assertEquals(1000, account.getBalance());
account = new Account(100);
assertEquals(100, account.getBalance());
}
@Test
public void testDeposit(){
setup();
account.deposit(1000);
assertEquals(11000, account.getBalance());
}
public void setup() {
account = new Account(10000);
}
@Test
public void testWithdraw(){
setup();
account.widthdraw(1000);
assertEquals(9000, account.getBalance());
}
}
// 3-8 정제
//package bank.test;
//
//import static org.junit.Assert.assertEquals;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// private Account account;
// @Test
// public void testAccount() throws Exception {
// setup();
// }
// @Test
// public void testGetBalance(){
// setup();
// assertEquals(10000, account.getBalance());
//
// account = new Account(1000);
// assertEquals(1000, account.getBalance());
//
// account = new Account(100);
// assertEquals(100, account.getBalance());
// }
// @Test
// public void testDeposit(){
// setup();
// account.deposit(1000);
// assertEquals(11000, account.getBalance());
// }
// public void setup() {
// account = new Account(10000);
// }
// @Test
// public void testWithdraw(){
// setup();
// account.widthdraw(1000);
// assertEquals(9000, account.getBalance());
// }
//}
// 3-8 errors, 3-8 Failures , 3-8 Success
//package bank.test;
//
//import static org.junit.Assert.assertEquals;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// assertEquals(10000, account.getBalance());
//
// account = new Account(1000);
// assertEquals(1000, account.getBalance());
//
// account = new Account(100);
// assertEquals(100, account.getBalance());
// }
// @Test
// public void testDeposit(){
// Account account = new Account(10000);
// account.deposit(1000);
// assertEquals(11000, account.getBalance());
// }
// @Test
// public void testWithdraw(){
// Account account = new Account(10000);
// account.widthdraw(1000);
// assertEquals(9000, account.getBalance());
// }
//}
// 3-7 assertEquals
//package bank.test;
//
//import static org.junit.Assert.assertEquals;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// assertEquals(10000, account.getBalance());
//
// account = new Account(1000);
// assertEquals(1000, account.getBalance());
//
// account = new Account(100);
// assertEquals(100, account.getBalance());
// }
//}
// 3-7 테스트 여러개 늘림, 3-7 여러 테스트를 만족시킴
//package bank.test;
//
//import static org.junit.Assert.fail;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// if (account.getBalance() != 10000) {
// fail();
// }
//
// account = new Account(1000);
// if (account.getBalance() != 1000) {
// fail();
// }
//
// account = new Account(100);
// if (account.getBalance() != 100) {
// fail();
// }
// }
//}
// 3-7 Failures 발생시키기, 3-7 성공
//package bank.test;
//
//import static org.junit.Assert.fail;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// if (account.getBalance() != 10000) {
// JUnit에서 에러 발생 시킬때 사용하는 메서드
// fail();
// throw new Exception();
// }
// }
//}
// 3-7 Errors 발생시키기
//package bank.test;
//
//import org.junit.Test;
//
//import bank.main.Account;
//
//public class AccountTest {
// @Test
// public void testAccount() throws Exception {
// }
// @Test
// public void testGetBalance(){
// Account account = new Account(10000);
// if(account.getBalance() != 10000){
// throw new Exception();
// }
// }
//}
// 3-6 실습
//package bank.test;
//import bank.main.Account;
//public class AccountTest {
// public void testAccount() throws Exception {
// Account account = new Account();
// if (account == null) {
// throw new Exception("계좌 생성 실패");
// }
// }
// public static void main(String[] args) {
// AccountTest test = new AccountTest();
// try {
// test.testAccount();
// } catch (Exception e) {
// System.out.println("실패(X)");
// return;
// }
// System.out.println("성공(O)");
// }
//}
-----------------------------------
/TDDPractice/src/bank/main/Account.java
package bank.main;
public class Account {
private int balance;
public Account(int money) {
balance = money;
}
public int getBalance() {
return balance;
}
public void deposit(int money) {
balance += money;
}
public void widthdraw(int money) {
balance -= money;
}
}
// 3-8 Success, 3-8 정제
//package bank.main;
//
//public class Account {
// private int balance;
// public Account(int money) {
// balance = money;
// }
//
// public int getBalance() {
// return balance;
// }
//
// public void deposit(int money) {
// balance += money;
// }
//
// public void widthdraw(int money) {
// balance -= money;
// }
//}
// 3-8 Failures
//package bank.main;
//
//public class Account {
// private int balance;
// public Account(int money) {
// balance = money;
// }
//
// public int getBalance() {
// return balance;
// }
//
// public void deposit(int money) {
// }
//
// public void widthdraw(int money) {
// }
//}
// 3-7 여러 테스트를 만족시킴, 3-8 errors
//package bank.main;
//
//public class Account {
// private int balance;
// public Account(int i) {
// balance = i;
// }
//
// public int getBalance() {
// return balance;
// }
//}
// 3-7 성공, 3-7 테스트 여러개 늘림
//package bank.main;
//
//public class Account {
//
// public Account(int i) {
// // TODO Auto-generated constructor stub
// }
//
// public int getBalance() {
// // TODO Auto-generated method stub
// return 10000;
// }
//}
// 3-7 Failures 발생시키기
//package bank.main;
//
//public class Account {
//
// public Account(int i) {
// // TODO Auto-generated constructor stub
// }
//
// public int getBalance() {
// // TODO Auto-generated method stub
// return 0;
// }
//}
//3-6 실습, 3-7 Errors 발생시키기
//package bank.main;
//public class Account {
//}
-----------------------------------
###################################
'OpenFrameWork' 카테고리의 다른 글
오픈프레임워크_Day88 (0) | 2015.07.17 |
---|---|
오픈프레임워크_Day87 (0) | 2015.07.16 |
오픈프레임워크_Day85 (0) | 2015.07.14 |
오픈프레임워크_Day84 (0) | 2015.07.13 |
오픈프레임워크_Day83 (0) | 2015.07.10 |