1. Make Tests Easy to Read and Maintain
function Test1() {
vector<ScoredDocument> docs;
docs.resize(5);
docs[0].url = "http://example.com";
docs[0].score = -5.0;
docs[1].url = "http://example.com";
docs[1].score = 1;
docs[2].url = "http://example.com";
docs[2].score = 4;
docs[3].url = "http://example.com";
docs[3].score = -99998.7;
docs[4].url = "http://example.com";
docs[4].score = 3.0;
SortAndFilterDocs(docs);
assert(docs.size() == 3);
assert(docs[0].score == 4);
assert(docs[1].score == 3.0);
assert(docs[2].score == 1);
/* There are at least eight different problems with this test code. By the end of the chapter, you’ll
be able to identify and fix all of them.*/
}
//As a first step in cleaning this up, we could create a helper function like:
function MakeScoredDoc( ScoredDocument , score, url) {
ScoredDocument.score = score;
ScoredDocument.url = url;
}
//Using this function, our test code becomes slightly more compact:
void function() {
vector<ScoredDocument> docs;
docs.resize(5);
MakeScoredDoc(docs[0], -5.0, "http://example.com");
MakeScoredDoc(docs[1], 1, "http://example.com");
MakeScoredDoc(docs[2], 4, "http://example.com");
MakeScoredDoc(docs[3], -99998.7, "http://example.com");
//...
}
void MakeScoredDoc( docs, score) {
var ScoredDocument ;
ScoredDocument.score = score;
ScoredDocument.url = "http://example.com";
docs.push_back(sd);
}
//Using this function, our test code is even more compact:
function Test1() {
vector<ScoredDocument> docs;
AddScoredDoc(docs, -5.0);
AddScoredDoc(docs, 1);
AddScoredDoc(docs, 4);
AddScoredDoc(docs, -99998.7);
//...
}
이 테스트는 어떤 점이 잘못되었을까?
// 'Doc'를 내림차순으로 정렬하고 점수가 0보다 작은 문서를 제거한다.
void SortAndFilterDocs(vector<ScoredDocument>* docs);
Void Tes1()
{
vector<ScoreDocument> docs;
docs.resize(50;
doc[0].url= "http://example.com":
doc[0].score = -5.0;
doc[1].url= "http://example.com":
doc[1].score = 1;
doc[2].url= "http://example.com":
doc[2].score = 4;
doc[3].url= "http://example.com":
doc[3].score = -99998.7;
doc[4].url= "http://example.com":
doc[4].score = 3;
SortAndFilterDocs(&docs);
assert(doc.size() == 3);
assert(docs[0].score = 4);
assert(docs[1].score = 3.0);
assert(docs[2].score = 1);
}
1. 덜 중요한 세부사항은 사용자가 볼 필요 없게 숨겨서 더 중요한 내용이 눈에 잘 띄게 해야한다.
=> Helper function을 작성
void MakeScoredDoc(ScoredDocument* sd, double score, string url)
{
sd->score = score;
sd-> url = url
}
void Test1()
{
vector<ScoreDocument> docs;
docs.resize(5);
MakeStoredDoc(&doc[0], -5.0 "http://exmaple.com");
MakeStoredDoc(&doc[1], 1 "http://exmaple.com");
MakeStoredDoc(&doc[2], 4 "http://exmaple.com");
MakeStoredDoc(&doc[3], -99998.7 "http://exmaple.com");
....
}
2. 아직 세부내용이 보인다. 예를 들어 "http://example.com"
=> Helper 함수가 더 많은 일을 하게 만들고 이름 addScoredDoc()
void AddScoredDoc(vector<ScoredDocument>& docs, double score)
{
ScoreDocument sd;
sd.score = score;
sd.url = "http://example.com";
docs.push_back(sd);
}
void Test1()
{
vector<ScoredDocument> docs;
AddScoredDoc(docs, -5.0);
AddScoredDoc(docs, 1);
AddScoredDoc(docs, 4);
AddScoredDoc(docs, -99998.7);
....
}
3. 최소한의 테스트 구문 만들기
- 생각을 코드로 만들기를 적용해서 쉬운 말로 작성해보자.
[5,1,4,-99998.7, 3] 과 같은 점스를 가지는 문서 리스트가 있다.
SortAndFilterDocs()를 호출한 다음에 문서는 ][4,3,1]이라는 순서대로 가져야 한다.
=> vector<ScoreDocument.에 대한 언급이 없다. 점수를 담는 배영 자체는 중요하지 않다.
- 이상적인 테스트 코드
CheckScoresBeforeAfter("-5,1,4, -99998.7, 3 " , "4,3,1");
4. 목적에 맞는 '미니-랭귀지' 구현하기
void CheckScoresBeforAfter(string input, string expected_output)
{
vector<Scoreddocument> docs = ScoredDocsFromString(input);
SortAndfilterDocs(&docs);
string output = ScoredDocsToString(docs);
assert(output == expected_output) ;
}
아래 helper 함수를 작성해야 한다.
vector<Scoreddocument>ScoredDocsFromString(string scores)
{
....
}
string ScoredDocsToString(vector<ScoredDocument> docs)
{
...
}
이제 전체 테스트를 단 한 번의 CheckScoresBeforAfter() 호출로 작성할 수 있다.
책에서는 분량이 많지만 기능이 강력하다고 하는데.. 이렇게 많이 적어야 하나. 테스트 코드를.ㅠㅠ
5. 읽기 편한 메시지 만들기
What if , assert(output == expected_output) 가 실패한다면 ?
Assertion failed: (output == expected_output) ,
function CheckScoreBeforeAfter, file text.cc, line 37 메시지가 뜬다면 expected_output 값이 무엇인지 궁금할 것이다.
부스트 C++ 라이브러이리에 포함된 BOOST_REQUIRE_EQUAL(output, expected_output) 함수를 쓰면
output == expected_output failed ["1,3,4 != 4,3,1"] 과 같은 메시지를 볼수 있을 것이다.
=> XUnit 처럼 도움이 되는 라이브러리 혹은 프레임워크가 있을 것이다 찾아서 사용하자.
6. 아니면 CheckScoreBeforeAfter() 에 실패할 경우 message를 보여줄 수 있도록 코드를 추가하자.
=> 핵심은 에러메시지가 최대한 유용해야한다.
void CheckScoresBeforeAfter(...)
{
...
if (output != expected_output) {
cerr << "CheckScoresBeforeAfter() failed, " <<
cerr << "input :" <<
cerr << "expected output : " <<
cerr << "Actual Output" <<
abort();
}
7. 좋은 테스트 값을 선택
CheckScoresBeforeAfter("2, 1, 3 " , "3, 2, 1"); // 기본적인 정렬
CheckScoresBeforeAfter("0 , -0.1, -10 " , "0"); // 0보다 작은 값을 모두 제거
CheckScoresBeforeAfter("1, -2, 1, -2" , "1, 1"); // 중복은 문제되지 않는다.
CheckScoresBeforeAfter("" , ""); // 빈 입력도 OK
8. Test 함수에 이름 붙이기.
void Test1()
{ }
=> void Test_SortAndFilterDocs()
{
}
테스트에 친숙한 개발