조회 함수의 단위 테스트
DOP에서 조회를 수행하는 메서드는 어떻게 테스트를 해야할까? Library.searchBooksByTitleJSON 메서드를 예시로 알아보자. 우선 테스트하려는 메서드에 의해 호출되는 함수들로 트리를 만든다. 꼭 트리를 만들지 않아도 머리 속으로 그려지면 된다. 트리의 가장 하단에 존재하는 메서드의 테스트가 가장 간단하다. 그 이유는 테스트할 부분이 메서드 메서드 자신밖에 없기 때문이다.
Catalog.authorNames = function (catalogData, authorIds) {
return _.map(authorIds, function (authorId) {
return _.get(catalogData, ["authorsById", authorId, "name"]);
});
};
authorNames 메서드를 테스트하는 건 간단하다. 파라미터 2개와 예상되는 결과를 준비하고 해당 메서드의 결과값과 비교하면 된다.
catalogData | authorIds | authorNames (예상 결과) |
저자가 둘인 장서 | [] | [] |
저자가 둘인 장서 | ["alan-moore"] | ["Alan Moore"] |
저자가 둘인 장서 | ["alan-moore", "dave-gibbons"] | ["Alan Moore", "Dave Gibbons"] |
// 완전한 형태의 catalogData
var catalogData = {
"booksByIsbn": {
"978-1779501127": {
"isbn": "978-1779501127",
"title": "Watchmen",
"publicationYear": 1987,
"authorIds": ["alan-moore", "dave-gibbons"],
"bookItems": [
{
"id": "book-item-1",
"libId": "nyc-central-lib",
"isLent": true
},
{
"id": "book-item-2",
"libId": "nyc-central-lib",
"isLent": false
}
]
}
},
"authorsById": {
"alan-moore": {
"name": "Alan Moore",
"bookIsbns": ["978-1779501127"]
},
"dave-gibbons": {
"name": "Dave Gibbons",
"bookIsbns": ["978-1779501127"]
}
}
}
// authorsName 테스트 맥락에서 유효한 데이터
var catalogData = {
"authorsById": {
"alan-moore": {
"name": "Alan Moore",
"bookIsbns": ["978-1779501127"]
},
"dave-gibbons": {
"name": "Dave Gibbons",
"bookIsbns": ["978-1779501127"]
}
}
}
catalogData는 위와 같이 완전한 형태를 준비하면 될까? 아니다! DOP에서 데이터의 유효성은 맥락에 의존한다. authorNames 메서드 실행을 위한 맥락에서는 아래 정도의 데이터만 유효한 데이터로 볼 수 있다. authorNames에서 필요한 데이터들이 모두 정의되어 있기 때문이다. 이런 방식으로 테스트 데이터를 준비하면 시간을 절약할 수 있다.
// lodash 불러오기
const _ = require('lodash');
// catalogData 객체 정의
var catalogData = {
"authorsById": {
"alan-moore": {
"name": "Alan Moore"
},
"dave-gibbons": {
"name": "Dave Gibbons"
}
}
};
// 테스트 예시
console.log(_.isEqual(Catalog.authorNames(catalogData, []), [])); // true
console.log(_.isEqual(Catalog.authorNames(catalogData, ["alan-moore"]), ["Alan Moore"])); // true
console.log(_.isEqual(Catalog.authorNames(catalogData, ["alan-moore", "dave-gibbons"]), ["Alan Moore", "Dave Gibbons"])); // true
책에서는 이와 동일한 방식으로 트리 하단부터 차례대로 올라가면 테스트 코드를 작성하기를 권장한다. 이 과정에서 상위에 있는 메서드를 테스트할 때는 이미 테스트 코드를 작성한 호출 메서드(자신보다 트리 하단에 위치한 메서드)는 모두 성공적으로 동작하는 상황임을 가정하고 테스트를 작성한다.
또 책에서 테스트할 때 신경써야할 2가지 포인트들을 제시하는데 아래와 같다. 2번의 경우 예시를 생각해보면 쉬운데 Json의 각 Key는 순서가 없는데, 단순히 반환 값을 비교한다면 예상 값과 실제 결과의 Key 순서가 달라지면 테스트가 실패할 수 있기 때문이다.
- 항상 실패 케이스를 생각하고 테스트할 것!
- 직렬화된 데이터(Json)를 반환한다면, 역직렬화하여 값을 비교할 것.
변경 함수의 단위 테스트
변경 함수도 마찬가지로 트리 함수로 시작한다. addMember 메서드를 테스트하기 위해 호출하는 메서드를 그리고 가장 하단의 메서드에 대한 단위 테스트로 시작한다.
var jessie = {
"email": "jessie@gmail.com",
"password": "my-secret"
};
var userManagementStateBefore = {
"membersByEmail": {
"franck@gmail.com": {
"email": "franck@gmail.com",
"password": "my-top-secret"
}
}
};
var expectedUserManagementStateAfter = {
"membersByEmail": {
"jessie@gmail.com": {
"email": "jessie@gmail.com",
"password": "my-secret"
},
"franck@gmail.com": {
"email": "franck@gmail.com",
"password": "my-top-secret"
}
}
};
var result = UserManagement.addMember(userManagementStateBefore, jessie);
_.isEqual(result, expectedUserManagementStateAfter)
이전과 크게 다르지 않다. 필요한 데이터와 예상 값을 기반으로 나온 결과물과 비교하는 방식이다. 변경의 경우 실패 케이스에 대한 테스트를 위해 Exception 테스트를 추가로 진행할 수도 있다. 조금 다른 고민이 있다면 결과를 반환하지 않는 메서드이다.
var jessie = {
"email": "jessie@gmail.com",
"password": "my-secret"
};
var libraryStateBefore = {
"userManagement": {
"membersByEmail": {
"franck@gmail.com": {
"email": "franck@gmail.com",
"password": "my-top-secret"
}
}
}
};
var expectedLibraryStateAfter = {
"userManagement": {
"membersByEmail": {
"jessie@gmail.com": {
"email": "jessie@gmail.com",
"password": "my-secret"
},
"franck@gmail.com": {
"email": "franck@gmail.com",
"password": "my-top-secret"
}
}
}
};
var systemState = new SystemState();
systemState.commit(null, libraryStateBefore);
System.addMember(libraryStateBefore, jessie);
_.isEqual(systemState.get(), expectedLibraryStateAfter);
addMember는 결과를 반환하지 않는다. 때문에 addMember 작업으로 변경된 데이터에 접근할 방법에 대한 추가적인 고민 정도가 필요하다.
내 생각
이번 장에서는 크게 새로운 내용은 없었던 것 같다. 데이터를 범용 자료 구조로 사용하기 때문에 테스트 데이터를 준비하기 수월하다는 생각이 들었지만 조금 더 생각해보니 원래도 필요 없는 데이터를 함께 메서드에 넘기고 있는 건 아닌가? 하는 생각도 들었다. 처음부터 필요한 데이터만 파라미터로 테스트 데이터 준비가 수월하다고 느낄 일도 없었을 것 같다.
'도서 > 기술' 카테고리의 다른 글
[책] 데이터 지향 프로그래밍 - 8장 고급 동시성 제어 (2) | 2024.12.21 |
---|---|
[책] 데이터 지향 프로그래밍 - 7장 기본 데이터 유효성 확인 (1) | 2024.12.15 |
[책] 데이터 지향 프로그래밍 - 5장 기본 동시성 제어 (1) | 2024.12.08 |
[책] 데이터 지향 프로그래밍 - 4장 상태 관리 (0) | 2024.12.01 |
[책] 데이터 지향 프로그래밍 - 3장 기본 데이터 조작 (4) | 2024.11.30 |