2011年2月9日 星期三

Development Driven by Tests

哲學: Development is driven by tests. You test first, then code. Until all the tests run, you aren't done. When all the tests run, and you can't think of any more tests that would break, you are done adding functionality.

例子: (Use case) 儲存/讀取遊戲設定。

先寫 test: (使用 Google C++ Testing Framework)
TEST(WinFileRepository, SetValue) {
const std::string name("options");
// Set
{
Repository* repository = new WinFileRepository(name);
repository->setValue("reset_level_button", "single_tap");
repository->setValue("offset", "off");
delete repository;
}

// Get
{
Repository* repository = new WinFileRepository(name);
EXPECT_TRUE(repository->value("reset_level_button") == "single_tap");
EXPECT_TRUE(repository->value("offset") == "off");
delete repository;
}
}

Compile failed. 用最簡單的方式實作,目的只是為了 pass compiling:
void WinFileRepository::setValue(...) {
}

std::string WinFileRepository::value(...) const {
return default_value;
}

Compile 過了,但是 test 不過。那就實作正確的程式碼吧:(略過細節)
// win_file_repository.h
class WinFileRepository : public Repository {
public:
WinFileRepository(...);
~WinFileRepository();

void setValue(...);
std::string value(...) const;

private:
void read();
void write();

std::string file_name_;
std::map map_;
std::string separator_;
std::string delimiter_;
};

// win_file_repository.cc
namespace {

bool ReadFileToString(...) { ... }
int WriteFile(...) { ... }

} // namespace

WinFileRepository::WinFileRepository(...)
: file_name_(name),
separator_(separator),
delimiter_(delimiter) {
read();
}

WinFileRepository::~WinFileRepository() {
write();
}

void WinFileRepository::setValue(...) {
map_[key] = value;
}

std::string WinFileRepository::value(...) const {
std::map::const_iterator it = map_.find(key);
if (it != map_.end()) {
return it->second;
} else {
return default_value;
}
}

void WinFileRepository::sync() {
write();
}

void WinFileRepository::read() {
std::string contents;
ReadFileToString(file_name_, &contents);

size_t prev_pos = 0;
for (size_t pos = contents.find(delimiter_, prev_pos);
pos != std::string::npos;
pos = contents.find(delimiter_, prev_pos)) {
const size_t sep_pos = contents.find(separator_, prev_pos);

map_[contents.substr(prev_pos, sep_pos - prev_pos)]
= contents.substr(sep_pos + 1, pos - sep_pos - 1);

pos++;
prev_pos = pos;
}
}

void WinFileRepository::write() {
std::string contents;
std::map::iterator it;
for (it = map_.begin(); it != map_.end(); it++) {
contents.append(it->first);
contents.append(separator_);
contents.append(it->second);
contents.append(delimiter_);
}

WriteFile(file_name_, contents.c_str(), static_cast(contents.size()));
}
最後真的 pass test 了,感動。以上大量參考 Chromium projects, Qt4...抄才是王道呀!當然實作很醜,要漂亮就得用 protocol buffer 或是 boost::serialization,但個人真的太懶了,能夠 pass tests 就夠了。

沒有留言:

張貼留言