728x90
의식적인 연습이란?
많은 연습은 역량을 보장하지 않는다. 그렇기에 온전히 집중하고 '의식적' 으로 행동할 것을 요구한다.
- 컴포트 존을 벗아난 지점에서 진행, 자신의 현재 능력을 살짝 넘어가는 작업을 지속적으로 시도한다.
- 명확하고 구체적인 목표를 가지고 진행한다.
- 피드백과 피드백에 따른 행동 변경을 수반한다. (코드리뷰, 짝 프로그래밍)
- 기존에 습득한 기술의 특정 부분을 집중적으로 개선함으로써 발전시키고, 수정하는 과정을 거친다.
자바 코드 컨벤션을 지키면서 프로그래밍한다.
indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다.
- while 문 안에 if문이 있으면 들여쓰기는 2이다.
- indent depth를 줄이는 좋은 방법은 함수를 분리하는 것이다.
함수가 한 가지 일만 하도록 최대한 작게 만든다.
자바지기님이 의식적인 연습
미션부여 -> 미션 완료후 PR -> PR 평가 후 공통 피드백 -> 미션2 제약사항 추가(리팩토링) -> 평가 -> 미션3 제약사항 추가 ...
리팩토링 자체가 의식적인 연습으로 TDD를 하는 과정이며, 그 과정에서 극단적인 미션을 추가하여 더욱 의식적인 연습을 돕는다.
TDD 리팩토링 (method)
TDD. 리팩토링 == 운동 : 평생동안 연습하겠다는 마음가짐으로 시작한다.
시작하기
- 주변 정리를 통해 꾸준히 연습할 시간을 확보한다.
- 토이 프로젝트를 시작하여 주변 환경에 영향을 받지 않고 꾸준히 연습한다. 작더라도 꾸준히!
1단계 - 단위 테스트로 연습하기
/* 동작 과정 테스트! */
@Test
public void split(){
String[] valeus = "1".split(",");
assertThat(values).contains("1");
values = "1,2".split(",");
assertThat(values).ContainsExactly("1","2");
}
@Test
public void substring(){
String input = "(1,2)";
String result = input.substring(1, input.length() - 1);
assertThat(result).isEqualsTo("1,2");
}
@Test
public void arrayList(){
ArrayList<String> list = new ArrayList<>();
list.add("first");
list.add("second");
assertThat(list.add("third")).isTrue();
assertThat(list.size()).isEqualsTo(3);
assertThat(list.get(0)).isEqualsTo("first");
contains... remove... size...
}
- 단위 테스트 방법을 학습할 수 있다.
- 단위 테스트 도구의 사용법을 익힐 수 있다.
- 사용하는 API에 대한 학습 효과가 있다.
2단계 - TDD 연습
- TDD 연습이 목적이니 난이도가 낮거나 자신에게 익숙한 문제로 시작한다.
- 엄청 간단한 예제도 상당히 많은 연습을 할 수 있으며 요구사항이 복잡할 필요는 없다.
- 의존관계를 가지지 않는 요구사항으로 연습한다. ex) 웹, 모바일, DB
package string_calc;
public class StringCalculator {
public static int splitAndSum(String text){
int result = 0;
if(text == null ||text.isEmpty()){
result = 0;
} else {
String[] values = text.split(",|:");
for(String value : values){
result += Integer.parseInt(value);
}
}
return result;
}
}
@Test
public void null_또는_빈값(){
assertThat(StringCalculator.splitAndSum(null)).isEqualsTo(0);
assertThat(StringCalculator.splitAndSum("")).isEqualsTo(0);
}
@Test
public void 값_하나(){
assertThat(StringCalculator.splitAndSum("1")).isEqualsTo(1);
}
@Test
public void 쉼표_구분자(){
assertThat(StringCalculator.splitAndSum("1,2")).isEqualsTo(3);
}
@Test
public void 쉼표_콜론_구분자(){
assertThat(StringCalculator.splitAndSum("1,2:3")).isEqualsTo(6);
}
3단계 - 메소드 분리 리팩토링
- 테스트 코드의 변경 없이 테스트 대상 코드를 개선하는 연습을 한다.
- 정상적인 기준보다는 정량적이고 측정 가능한 방법으로 연습한다.
package string_calc;
public class StringCalculator {
// 1. 들여쓰기(2depth)를 제거하고 else를 제거
// 2. 메소드에서 한 가지 일만 하도록 변경, 로컬 변수 제거
// 3. 컴포즈 패턴 메소드 적용. (1단계 추상화)
public static int splitAndSum(String text) {
if (isBlank(text)) {
return 0;
}
return sum(toInts(split(text)));
}
private static boolean isBlank(String text){
return text == null || text.isEmpty();
}
private static String[] split(String text){
return text.split(",|:");
}
private static int[] toInts(String[] values) {
int numbers[] = new int[values.length];
for (int i = 0; i < values.length; i++) {
numbers[i] = Integer.parseInt(values[i]);
}
return numbers;
}
private static int sum(int[] numbers) {
int result = 0;
for (int number : numbers) {
result += number;
}
return result;
}
}
- 연습은 극단적인 방법으로 하는 것도 좋다. 단 한번에 한가지 씩 !
- 메소드의 라인 수를 제한하며 연습하는 것도 좋은 방법이다.
TDD 리팩토링 (class)
package string_calc;
/* 양수를 받는 숫자 객체 */
public class Positive {
private int number;
public Positive(String number){
this(Integer.parseInt(number));
}
public Positive(int number){
if(number < 0){
throw new RuntimeException();
}
this.number = number;
}
public Positive add(Positive other){
return new Positive(this.number + other.number);
}
public int getNumber(){
return number;
}
}
package string_calc;
public class StringCalculator {
// 1. 들여쓰기(2depth)를 제거하고 else를 제거
// 2. 메소드에서 한 가지 일만 하도록 변경, 로컬 변수 제거
// 3. 컴포즈 패턴 메소드 적용. (1단계 추상화)
// 4. 음수가 들어오면 RuntimeException, 양수를 받는 숫자 객체 추가
public static int splitAndSum(String text) {
if (isBlank(text)) {
return 0;
}
return sum(toInts(split(text)));
}
private static boolean isBlank(String text){
return text == null || text.isEmpty();
}
private static String[] split(String text){
return text.split(",|:");
}
private static Positive[] toInts(String[] values) {
Positive[] positives = new Positive[values.length];
for (int i = 0; i < values.length; i++) {
positives[i] = new Positive(values[i]);
}
return positives;
}
private static int sum(Positive[] numbers) {
Positive result = new Positive(0);
for (Positive number : numbers) {
result = result.add(number);
}
return result.getNumber();
}
}
@Test
public void 음수(){
Assertions.assertThrows(RuntimeException.class, ()-> {
StringCalculator.splitAndSum("-1:2,3");
});
}
클래스 분리 연습을 위해 활용할 수 있는 원칙
- 일급 컬렉션을 쓴다. ex) List<Positive>
- 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다. (극단적 연습)
장난감 프로젝트 난이도 높이기
- 점진적으로 요구사항이 복잡한 프로그램을 구현한다.
- 앞에서 지켰던 기준을 지키면서 프로그래밍 연습을 한다.
리팩토링 연습하기 좋은 프로그램 요구사항
- 게임과 같이 요구사항이 명확한 프로그램으로 연습
- 의존관계가 없는 프로그램
- 약간은 복잡한 로직이 있는 프로그램
- ex) 로또, 사다리 타기, 볼링 게임 점수판, 체스 게임, 지뢰 찾기 게임 : UI는 콘솔
의존관계 추가를 통한 난이도 높이기 (DB, UI, 웹, 모바일 등등..)
- 테스트하기 쉬운 코드와 테스트하기 어려운 코드를 분리하는 눈이 필요
- 테스트하기 어려운 코드를 테스트 하기 쉬운 코드로 설계하는 감이 필요
한 단계 더 나아간 연습을 하고 싶다면..?
- 컴파일 에러를 최소화하면서 리팩토링하기
- ATDD 기반으로 응용 어플리케이션 개발하기
- 레거시 어플리케이션에 테스트 코드 추가하여 리팩토링하기
정리
TDD. 리팩토링 적용이 실패하는 이유
TDD, 리팩토링 연습이 충분하지 않는 상태에서 `레거시 어플리케이션에 테스트 코드를 추가해 리팩토링하기` 같은 높은 난이도에 도전한다.
도움이 되는 책
객체지향 생활체조 원칙, 클린 코드
TDD. 리팩토링 연습을 위해 필요한 것은?
- 조급한 마음 대신 여유롭게..
- 나만의 토이 프로젝트
- 같은 과제를 반복적으로 구현할 수 있는 인내력, 꾸준함, 성실함
팀에 TDD를 전파하기 위해선?
- 내가 맡은 기능 구현에 TDD, 리팩토링 적용 -> 전파
- 내가 구현한 코드 또는 동료의 관심에서 작은 성공을 맛본다.
리더로써 TDD를 전파하기 위해선?
- 1:1 공략
- 팀원이 개선할 부분을 말하고, 해결책을 제안하도록 유도
'공부 > TDD' 카테고리의 다른 글
볼링게임 TDD [2] (0) | 2020.10.02 |
---|---|
볼링게임 TDD [1] (0) | 2020.09.30 |
[OKKYCON: 2018] 이규원 - 당신들의 TDD가 실패하는 이유 (0) | 2020.09.26 |
테스트 주도 개발 [10 - 17장] (0) | 2020.09.12 |
테스트 주도 개발 [1 - 9장] (0) | 2020.09.05 |
댓글