SOFTWARE ENGINEERING/Art Of Readable Code

Ch14 Testing and Readability

파란실버라이트 2012. 11. 23. 13:03

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()

{



 테스트에 친숙한 개발