Download - ユニットテストの保守性を作りこむ, xpjugkansai2011
![Page 1: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/1.jpg)
ユニットテストの 保守性を作りこむ
~設計・実装の工夫で支える ユニットテストの継続的活用~
rev.1.1 2011/1/29 @XP祭り関西2011
井芹 洋輝
![Page 2: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/2.jpg)
謝辞
• 登壇者として声をかけていただいた細谷さん、
• 機会を提供して頂いたXPJUG関西の皆様
• 素晴らしい場を作り上げている登壇者・参加者・運営者の方々
深くお礼申し上げます。
![Page 3: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/3.jpg)
自己紹介
• 井芹洋輝(いせりひろき)
• 扱っているもの
– 組込み開発/ソフトウェアテスト/開発者テスト
• 所属
– WACATE実行委員/TDD研究会/ATECなど
• 対外活動
– JaSST’11 Tokyo/WACATE2011冬/Androidテスト祭り等 – ソフトウェアテストPRESS総集編/Ultimate agile Stories
![Page 4: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/4.jpg)
概要
• ユニットテストの保守性を作りこむアプローチを、いくつかの具体例を交えて俯瞰的に見ていきます
![Page 5: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/5.jpg)
概要
1. 背景 – ユニットテストの現状と課題
2. 目標 – ユニットテスト継続活用における目標
3. ユニットテストの保守性向上アプローチ – テストコードの実装の工夫
– テストコードの構造設計の工夫
– テスト設計の工夫
4. まとめ
![Page 6: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/6.jpg)
背景
![Page 7: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/7.jpg)
ユニットテストの用途 【現在編】
![Page 8: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/8.jpg)
軽快なチェック
![Page 9: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/9.jpg)
//コードから無視する文字を削除する
string reformat(string line)
{
...(複雑なロジック)...
}
複雑なメソッドを書いた きちんと動くか不安だ
![Page 10: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/10.jpg)
TEST(CodeAnalyzer, reformatContainComment)
{
EXPECT_EQ("#hoge", reformat("#hoge /* fuga */"));
}
TEST(CodeAnalyzer, reformatContainSpace)
{
EXPECT_EQ("void function()", reformat("void function ()"));
}
TEST(CodeAnalyzer, reformatContainEmptyLine)
{
EXPECT_EQ("test;¥n", reformat("test;¥n¥n¥n¥n"));
}
…
//コードから無視する文字を削除する
string reformat(string line)
{
...(複雑なロジック)...
}
安心できるまでテストを書く
![Page 11: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/11.jpg)
TEST(CodeAnalyzer, reformatContainComment)
{
EXPECT_EQ("#hoge", reformat("#hoge /* fuga */"));
}
TEST(CodeAnalyzer, reformatContainSpace)
{
EXPECT_EQ("void function()", reformat("void function ()"));
}
TEST(CodeAnalyzer, reformatContainEmptyLine)
{
EXPECT_EQ("test;¥n", reformat("test;¥n¥n¥n¥n"));
}
…
//コードから無視する文字を削除する
string reformat(string line)
{
...(複雑なロジック)...
}
安心できるまでテストを書く
リスクや不安を即時に解消して前進できる
![Page 12: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/12.jpg)
リファクタリング
![Page 13: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/13.jpg)
//うるう年か判定する
bool isLeapYear(unsigned int year)
{
if (year % 4 == 0)
{
if (year % 100 == 0)
{
if (year % 400 == 0)
{
return true;
}
return false;
}
return true;
}
return false;
}
実装が汚い
![Page 14: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/14.jpg)
TEST(TestYear, isLeapYearDivisibleBy4)
{
EXPECT_EQ(true, isLeapYear(8));
EXPECT_EQ(false, isLeapYear(9));
EXPECT_EQ(true, isLeapYear(0));
}
TEST(TestYear, isLeapYearDivisibleBy400)
{
EXPECT_EQ(true, isLeapYear(800));
EXPECT_EQ(false, isLeapYear(900));
}
TEST(TestYear, isLeapYearDivisibleBy100)
{
EXPECT_EQ(false, isLeapYear(500));
}
//うるう年か判定する
bool isLeapYear(unsigned int year)
{
if (year % 4 == 0)
{
if (year % 100 == 0)
{
if (year % 400 == 0)
{
return true;
}
return false;
}
return true;
}
return false;
}
1. テストで保護する
![Page 15: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/15.jpg)
//うるう年か判定する
bool isLeapYear(unsigned int year)
{
if (year % 400 == 0)
{
return true;
}
if ((year % 4 == 0) && (year % 100 != 0))
{
return true;
}
return false;
}
2. テストでチェックしつつ
リファクタリング
TEST(TestYear, isLeapYearDivisibleBy4)
{
EXPECT_EQ(true, isLeapYear(8));
EXPECT_EQ(false, isLeapYear(9));
EXPECT_EQ(true, isLeapYear(0));
}
TEST(TestYear, isLeapYearDivisibleBy400)
{
EXPECT_EQ(true, isLeapYear(800));
EXPECT_EQ(false, isLeapYear(900));
}
TEST(TestYear, isLeapYearDivisibleBy100)
{
EXPECT_EQ(false, isLeapYear(500));
}
1. テストで保護する
![Page 16: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/16.jpg)
//うるう年か判定する
bool isLeapYear(unsigned int year)
{
if (year % 400 == 0)
{
return true;
}
if ((year % 4 == 0) && (year % 100 != 0))
{
return true;
}
return false;
}
2. テストでチェックしつつ
リファクタリング
TEST(TestYear, isLeapYearDivisibleBy4)
{
EXPECT_EQ(true, isLeapYear(8));
EXPECT_EQ(false, isLeapYear(9));
EXPECT_EQ(true, isLeapYear(0));
}
TEST(TestYear, isLeapYearDivisibleBy400)
{
EXPECT_EQ(true, isLeapYear(800));
EXPECT_EQ(false, isLeapYear(900));
}
TEST(TestYear, isLeapYearDivisibleBy100)
{
EXPECT_EQ(false, isLeapYear(500));
}
1. テストで保護する
安全に記述改善
![Page 17: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/17.jpg)
学習テスト
![Page 18: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/18.jpg)
boost::xpressive
初使用で よくわからない
![Page 19: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/19.jpg)
boost::xpressive
TEST(regex, regexFileseek)
{
namespace xp = boost::xpressive;
string target; xp::smatch match;
xp::sregex rex = xp::sregex::compile ("(¥¥.c|¥¥.h)$");
target = "test";
EXPECT_EQ(false, xp::regex_search(target, match, rex));
target = "test.c";
EXPECT_EQ(true, xp::regex_search(target, match, rex));
target = "test.h";
EXPECT_EQ(true, xp::regex_search(target, match, rex));
target = "./../source/_svn/text-base/main.c.svn-base";
EXPECT_EQ(false, xp::regex_search(target, match, rex));
target = "test.cpp";
EXPECT_EQ(false, xp::regex_search(target, match, rex));
}
テストで動作を確認する。不安がなくなるまで試す
![Page 20: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/20.jpg)
boost::xpressive
TEST(regex, regexFileseek)
{
namespace xp = boost::xpressive;
string target; xp::smatch match;
xp::sregex rex = xp::sregex::compile ("(¥¥.c|¥¥.h)$");
target = "test";
EXPECT_EQ(false, xp::regex_search(target, match, rex));
target = "test.c";
EXPECT_EQ(true, xp::regex_search(target, match, rex));
target = "test.h";
EXPECT_EQ(true, xp::regex_search(target, match, rex));
target = "./../source/_svn/text-base/main.c.svn-base";
EXPECT_EQ(false, xp::regex_search(target, match, rex));
target = "test.cpp";
EXPECT_EQ(false, xp::regex_search(target, match, rex));
}
テストで動作を確認する。不安がなくなるまで試す
動作の勉強とチェック
コードのドキュメントとして活用
![Page 21: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/21.jpg)
デバッギングテスト
![Page 22: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/22.jpg)
class MacroWord
バグがでた
![Page 23: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/23.jpg)
TEST_F(TestMacroWord, macroDataMacroWordBug21)
{
MacroData macroData;
macroData.push_back("AUTO_DEBUG_1");
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back(“BB_H_");
MacroWord macroWord(macroData);
EXPECT_EQ("AUTO_DEBUG_1", macroWord.data_[0].macroName_);
EXPECT_EQ(1, analyzer.data_[0].number);
EXPECT_EQ("AUTO_DEBUG_2", macroWord.data_[1].macroName_)
EXPECT_EQ(3, analyzer.data_[1].number);
EXPECT_EQ(“BB_H_", macroWord.data_[2].macroName_);
EXPECT_EQ(1, analyzer.data_[2].number);
}
class MacroWord
1. テスト上でバグを再現する
![Page 24: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/24.jpg)
TEST_F(TestMacroWord, MacroWordBug21)
{
MacroData macroData;
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back("AUTO_DEBUG_2");
MacroWordProxy::wordCount(macroData);
EXPECT_EQ(3, MacroWordProxy::getSize(0));
}
class MacroWord
1. テスト上でバグを再現する
2. テストでバグを絞込み特定する
![Page 25: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/25.jpg)
TEST_F(TestMacroWord, MacroWordBug21)
{
MacroData macroData;
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back("AUTO_DEBUG_2");
MacroWordProxy::wordCount(macroData);
EXPECT_EQ(3, MacroWordProxy::getSize(0));
}
class MacroWord
1. テスト上でバグを再現する
2. テストでバグを絞込み特定する
3. 修正後テストがパスすることを確認する
![Page 26: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/26.jpg)
TEST_F(TestMacroWord, macroWordBug21)
{
MacroData macroData;
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back("AUTO_DEBUG_2");
macroData.push_back("AUTO_DEBUG_2");
MacroWordProxy::wordCount(macroData);
EXPECT_EQ(3, MacroWordProxy::getSize(0));
}
class MacroWord
1. テスト上でバグを再現する
2. テストでバグを絞込み特定する
3. 修正後テストがパスすることを確認する
バグを再現 バグを特定 バグ修正のチェック
![Page 27: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/27.jpg)
その他ユニットテストの活用
テスト駆動開発
CIによる自動回帰テスト
RED
GREEN REFACT
OR
![Page 28: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/28.jpg)
ユニットテストの効能
• バグを検出する
• バグ発生箇所を絞り込む
• バグ混入を監視する
• 機能的な保証を行う
• 設計支援を行う
• ドキュメントとなる
![Page 29: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/29.jpg)
ツール・方法論が発達した今ではプログラミングとユニットテストが一体となり、プログラマはプログラミングの最初から最後まで継続的にユニットテストのサポートを受けられるようになっている
![Page 30: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/30.jpg)
ユニットテストの継続活用 における課題
![Page 31: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/31.jpg)
ユニットテストの継続活用においては保守性がクリティカルな問題となりえる 保守性に問題を持つユニットテストは、再利用の度に冗長なコストを累積させる
![Page 32: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/32.jpg)
可読性の悪いテスト
![Page 33: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/33.jpg)
TEST(TestHoge, Hoge)
{
stringstream ss;
int i, j;
Inspector inspector("hoge", "fuga");
EXPECT_EQ(false, inspector.isEmpty());
inspector.pushLine("this is test.");
inspector.pushLine("this is test.");
EXPECT_EQ(6, inspector.getWords());
for (j = 0; j < 100; j++) {
for (i = 0; i < j; i++) {
ss << j;
ss << " ";
ss << i;
inspector.pushLine(ss.str());
EXPECT_EQ(INFO, inspector.getState()); }
inspector.addSection();
EXPECT_EQ(j, inspector.getSection()); }
EXPECT_EQ(0, inspector.runInspection());
}
![Page 34: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/34.jpg)
TEST(TestHoge, Hoge)
{
stringstream ss;
int i, j;
Inspector inspector("hoge", "fuga");
EXPECT_EQ(false, inspector.isEmpty());
inspector.pushLine("this is test.");
inspector.pushLine("this is test.");
EXPECT_EQ(6, inspector.getWords());
for (j = 0; j < 100; j++) {
for (i = 0; i < j; i++) {
ss << j;
ss << " ";
ss << i;
inspector.pushLine(ss.str());
EXPECT_EQ(INFO, inspector.getState()); }
inspector.addSection();
EXPECT_EQ(j, inspector.getSection()); }
EXPECT_EQ(0, inspector.runInspection());
}
何を検証しているのか分からない
バグはどこにあるのか分からない
Assertion Roulette
正しいのかどうか わからない
![Page 35: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/35.jpg)
TEST(TestHoge, Hoge)
{
stringstream ss;
int i, j;
Inspector inspector("hoge", "fuga");
EXPECT_EQ(false, inspector.isEmpty());
inspector.pushLine("this is test.");
inspector.pushLine("this is test.");
EXPECT_EQ(6, inspector.getWords());
for (j = 0; j < 100; j++) {
for (i = 0; i < j; i++) {
ss << j;
ss << " ";
ss << i;
inspector.pushLine(ss.str());
EXPECT_EQ(INFO, inspector.getState()); }
inspector.addSection();
EXPECT_EQ(j, inspector.getSection()); }
EXPECT_EQ(0, inspector.runInspection());
}
何を検証しているのか分からない
バグはどこにあるのか分からない
Assertion Roulette
テストの再利用コストを増大させる
正しいのかどうか わからない
![Page 36: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/36.jpg)
変更性の悪いテスト (テストコードの重複/
Fragile Test)
![Page 37: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/37.jpg)
TEST(testHoge, InspectionFugaPiyo)
{
MotorStatus motorStatus(0, 0);
MaintenanceData mtData;
MaintenanceType mtType(createRegionID(EU));
setInitialData(mtData, mtType);
InspectionFuga inspector;
inspector.set(createMaintenanceInfo(mtData, mtType), motorStatus);
…
}
TEST(testHoge, InspectionFugaFuga)
{
MotorStatus motorStatus(-1, 2);
MaintenanceData mtData;
MaintenanceType mtType(createRegionID(ASIA));
setInitialData(mtData, mtType);
InspectionFuga inspector;
inspector.set(createMaintenanceInfo(mtData, mtType), motorStatus);
…
}
![Page 38: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/38.jpg)
TEST(testHoge, InspectionFugaHoge)
{
MotorStatus motorStatus(133, 232);
MaintenanceData mtData;
MaintenanceType mtType(createRegionID(JAPAN));
setInitialData(mtData, mtType);
InspectionFuga inspector;
inspector.set(createMaintenanceInfo(mtData, mtType), motorStatus);
…
}
似たような記述が多数存在・・
![Page 39: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/39.jpg)
TEST(testHoge, InspectionFugaHoge)
{
MotorStatus motorStatus(133, 232);
MaintenanceData mtData;
MaintenanceType mtType(createRegionID(JAPAN));
setInitialData(mtData, mtType);
InspectionFuga inspector;
inspector.set(createMaintenanceInfo(mtData, mtType), motorStatus);
…
}
Fragile Test
テストが製品コードの変更コストを増大させる
テストがテストコードの変更コストを増大させる
テストコードの変更箇所も特定困難
製品コードの変更コストが大きい
![Page 40: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/40.jpg)
補足(Fragile Test)
• 製品コードとテストコードの過度な依存関係によって、製品コード/テストコードの変更コストが増大してしまう問題をFragile Testsという
• Fragile Tests問題は継続的なユニットテストやTDDが抱える大きな問題であり、常に対策し続けなくてはならない
![Page 41: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/41.jpg)
テストコードもレガシーコード化する ユニットテストを継続活用する環境では、レガシーコード化したユニットテストは最悪テストの効果をマイナスに反転させる
![Page 42: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/42.jpg)
ユニットテストのコストモデル (継続的にテストを作る場合)
時間
ユニットテスト
規模
時間
ユニットテスト
の利益
時間
ユニットテスト
保守コスト
![Page 43: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/43.jpg)
時間
ユニットテスト
規模
時間
ユニットテスト
の利益
時間
ユニットテスト
保守コスト
レガシーコード化
レガシーコード化
ユニットテストのコストモデル (継続的にテストを作る場合)
![Page 44: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/44.jpg)
時間
ユニットテスト
規模
時間
ユニットテスト
の利益
時間
ユニットテスト
保守コスト
レガシーコード化
レガシーコード化
使えないテストの放棄という手段に・・・
ユニットテストのコストモデル (継続的にテストを作る場合)
![Page 45: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/45.jpg)
ユニットテストはプログラミングを強力に支援し得るが、一方でプログラミングを強力に阻害し得る ユニットテストはソフトウェアの保守性を支えるが、一方でソフトウェアの保守性を阻害し得る
![Page 46: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/46.jpg)
ユニットテストの継続活用 のための目標
![Page 47: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/47.jpg)
目標
時間
ユニットテスト
規模
時間
ユニットテスト
の利益
時間
ユニットテスト
運用コスト
保守性改善
![Page 48: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/48.jpg)
目標
時間
ユニットテスト
規模
時間
ユニットテスト
の利益
時間
ユニットテスト
運用コスト
保守性改善
保守性改善により
保守コストの増大を抑える
![Page 49: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/49.jpg)
テストとプロダクトの扱いに違いはない。テストもプロセスを持ち構造的設計、機能的設計を行う必要がある。テストはプロダクトと同じように保守性を作りこむ余地がある。
![Page 50: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/50.jpg)
テストコードの
構造設計
テストコードの
実装
テストの
上位設計
テスト設計
テスト実装
テスト設計 テスト構造設計
テストコードの実装
![Page 51: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/51.jpg)
ユニットテストの 保守性を支える原則
• 変更に対する堅牢性に優れる
• 可読性に優れる
• 独立性に優れる
• 自己完結している
• 完全自動化している
• 細粒度なテストケース
![Page 52: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/52.jpg)
ユニットテストの 保守性を支える原則
• 変更に対する堅牢性に優れる
• 可読性に優れる
• 独立性に優れる
• 自己完結している
• 完全自動化している
• 細粒度なテストケース
テスト対象・テスト設計が変更されても、テストコードの変更が小さく済む
![Page 53: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/53.jpg)
ユニットテストの 保守性を支える原則
• 変更に対する堅牢性に優れる
• 可読性に優れる
• 独立性に優れる
• 自己完結している
• 完全自動化している
• 細粒度なテストケース
テストコードからテストの設計や意図を容易に読み取れる
![Page 54: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/54.jpg)
ユニットテストの 保守性を支える原則
• 変更に対する堅牢性に優れる
• 可読性に優れる
• 独立性に優れる
• 自己完結している
• 完全自動化している
• 細粒度なテストケース
構造軸:他のテストメソッド、テストクラスの影響を受けない 時間軸:テストの実行順序の影響を受けない
![Page 55: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/55.jpg)
ユニットテストの 保守性を支える原則
• 変更に対する堅牢性に優れる
• 可読性に優れる
• 独立性に優れる
• 自己完結している
• 完全自動化している
• 細粒度なテストケース
Setup、テスト実行、結果の検証、Teardownの一連の処理が細か粒度で完結している
![Page 56: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/56.jpg)
ユニットテストの 保守性を支える原則
• 変更に対する堅牢性に優れる
• 可読性に優れる
• 独立性に優れる
• 自己完結している
• 完全自動化している
• 細粒度なテストケース
テストを実行する処理を完全に自動化できる
![Page 57: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/57.jpg)
ユニットテストの 保守性を支える原則
• 変更に対する堅牢性に優れる
• 可読性に優れる
• 独立性に優れる
• 自己完結している
• 完全自動化している
• 細粒度なテストケース テストメソッドが十分に細分化されている
![Page 58: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/58.jpg)
ユニットテストの 保守性作りこみアプローチ
1. 実装の工夫
2. 構造設計の工夫
3. 設計の工夫
![Page 59: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/59.jpg)
ユニットテストの保守性作りこみアプローチ1
実装の工夫
![Page 60: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/60.jpg)
ユニットテストの実装はプログラミング行為そのものである 保守性を高めるパターンを適用し継続的に記述改善する必要がある
![Page 61: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/61.jpg)
コードの共通化
• 重複する記述は共通化する
• 便利なロジックは汎用化する
![Page 62: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/62.jpg)
TEST(Buyer, addSameStatus)
{
Buyer buyer;
Customer customer1("Taro", "Yamada", 15, 2, "HOGE|FUGA");
customer1.addCategory(STATE_ACTIVE);
Customer customer2("Taro", "Yamada", 15, 2, "HOGE|FUGA|HOGEHOGE");
customer2.addCategory(STATE_ACTIVE);
buyer.add(customer1);
buyer.add(customer2);
EXPECT_EQ(0, buyer.getSection());
}
似た記述が大量に散在
![Page 63: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/63.jpg)
TEST(Buyer, addSameStatus)
{
Buyer buyer;
Customer customer1("Taro", "Yamada", 15, 2, "HOGE|FUGA");
customer1.addCategory(STATE_ACTIVE);
Customer customer2("Taro", "Yamada", 15, 2, "HOGE|FUGA|HOGEHOGE");
customer2.addCategory(STATE_ACTIVE);
buyer.add(customer1);
buyer.add(customer2);
EXPECT_EQ(0, buyer.getSection());
}
似た記述が大量に散在
Customer createCustomer(string status)
{
Customer customer("Taro", "Yamada", 15, 2, status);
customer.addCategory(STATE_ACTIVE);
return customer;
}
Parameterized Creation Method
![Page 64: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/64.jpg)
TEST(Buyer, addSameStatus)
{
Buyer buyer;
Customer customer1 = createCustomer("HOGE|FUGA");
Customer customer2 = createCustomer("HOGE|FUGA|HOGEHOGE");
buyer.add(customer1);
buyer.add(customer2);
EXPECT_EQ(0, buyer.getSection());
}
Customer createCustomer(string status)
{
Customer customer("Taro", "Yamada", 15, 2, status);
customer.addCategory(STATE_ACTIVE);
return customer;
}
Parameterized Creation Method
重複部分をCreation Methodで共通化
![Page 65: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/65.jpg)
TEST(TestItemList, ItemPush)
{
…
EXPECT_EQ(expectItem.getName(), item.getName());
EXPECT_EQ(expectItem.getID(), item.getID());
EXPECT_EQ(expectItem.getSize(), item.getSize());
}
TEST(TestItemList, itemPush)
{
…
EXPECT_EQ(expectItem2.getName(), item2.getName());
EXPECT_EQ(expectItem2.getID(), item2.getID());
EXPECT_EQ(expectItem2.getSize(), item2.getSize());
}
似た記述が大量に散在
![Page 66: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/66.jpg)
TEST(TestItemList, itemPush)
{
…
EXPECT_EQ(expectItem.getName(), item.getName());
EXPECT_EQ(expectItem.getID(), item.getID());
EXPECT_EQ(expectItem.getSize(), item.getSize());
}
TEST(TestItemList, itemPush)
{
…
EXPECT_EQ(expectItem2.getName(), item2.getName());
EXPECT_EQ(expectItem2.getID(), item2.getID());
EXPECT_EQ(expectItem2.getSize(), item2.getSize());
}
似た記述が大量に散在
#define EXPECT_ITEM_EQ(expect, actual) ¥
EXPECT_EQ((expect).getName(), (actual).getName()); ¥
EXPECT_EQ((expect).getID(), (actual).getID()+1); ¥
EXPECT_EQ((expect).getSize(), (actual).getSize());
Custom Assertion
![Page 67: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/67.jpg)
#define EXPECT_ITEM_EQ(expect, actual) ¥
EXPECT_EQ((expect).getName(), (actual).getName()); ¥
EXPECT_EQ((expect).getID(), (actual).getID()+1); ¥
EXPECT_EQ((expect).getSize(), (actual).getSize());
Custom Assertion
TEST(TestItemList, itemPush)
{
…
EXPECT_ITEM_EQ(expectItem, item);
}
TEST(TestItemList, test_itemPush)
{
…
EXPECT_ITEM_EQ(expectItem2, item2);
}
重複部分を拡張Assertionで共通化
![Page 68: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/68.jpg)
可読性の向上
• 重要なものを目立たせる
• 適切な名前に置き換える
• 小さくする
![Page 69: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/69.jpg)
Customer customer2("Taro", "Yamada", 15, 2, "HOGE|FUGA|HOGEHOGE");
何でもいいダミー値 テストしたい値
![Page 70: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/70.jpg)
Customer customer2("Taro", "Yamada", 15, 2, "HOGE|FUGA|HOGEHOGE");
Customer customer2 = createCustomer("HOGE|FUGA|HOGEHOGE");
Parameterized Creation Method
何でもいいダミー値 テストしたい値
ダミー値を隠し、重要なパラメータを強調する
重要なものを目立たせる
※隠ぺいはトレードオフなので要注意
![Page 71: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/71.jpg)
TEST(testHoge, Fuga)
{
MotorStatus motorStatus(133, 232);
InspectionFuga inspector;
inspector.set(createMaintenanceInfo(motorStatus);
EXPECT_EQ(START, inspector.getState());
EXPECT_EQ(true, inspector.isEmpty());
inspector.initialize();
EXPECT_EQ(INFO, inspector.getState());
EXPECT_EQ(false, inspector.isEmpty());
}
![Page 72: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/72.jpg)
TEST(testHoge, Fuga)
{
MotorStatus motorStatus(133, 232);
InspectionFuga inspector;
inspector.set(createMaintenanceInfo(motorStatus);
EXPECT_EQ(START, inspector.getState());
EXPECT_EQ(true, inspector.isEmpty());
inspector.initialize();
EXPECT_EQ(INFO, inspector.getState());
EXPECT_EQ(false, inspector.isEmpty());
}
1つのテストメソッドで色々検証しているため少し読みにくい
分割で改善する
![Page 73: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/73.jpg)
TEST(testHoge, Fuga)
{
InspectionFuga inspector = createInspectionFugaDummy();
EXPECT_EQ(START, inspector.getState());
EXPECT_EQ(true, inspector.isEmpty());
inspector.initialize();
EXPECT_EQ(INFO, inspector.getState());
EXPECT_EQ(false, inspector.isEmpty());
}
分割時に重複するメソッドをあらかじめ抜き出す
![Page 74: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/74.jpg)
TEST(testHoge, fugaConstractor)
{
InspectionFuga inspector = createInspectionFugaDummy();
EXPECT_EQ(START, inspector.getState());
EXPECT_EQ(true, inspector.isEmpty());
}
TEST(testHoge, fugaInitialize)
{
InspectionFuga inspector = createInspectionFugaDummy();
inspector.initialize();
EXPECT_EQ(INFO, inspector.getState());
EXPECT_EQ(false, inspector.isEmpty());
}
テストメソッドを小さくする
意図を読み取りやすい名前をつける
![Page 75: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/75.jpg)
その他工夫
• テストメソッドをコンパクトに記述する
• テストコードの影響範囲を限定する
– 時間軸、構造軸
• 危ういコードを分離する
– 変更頻度の高いインターフェースをラッピングする
– テスト用インタフェースを用意する
![Page 76: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/76.jpg)
その他工夫
• テストコードの保守性は製品コード・テストコード両方から支えられる
• 堅牢なユニットテストには堅牢な製品コードのインターフェースが不可欠である
製品 コード
テスト コード
テストが内部メンバを細かく参照する形は
Fragile Testsになりえる
![Page 77: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/77.jpg)
その他工夫
• テストコードの保守性は製品コード・テストコード両方から支えられる
• 堅牢なユニットテストには堅牢な製品コードのインターフェースが不可欠である
製品 コード
テスト コード
堅牢なインターフェースを製品コードが提供することで
テストコードの堅牢性を高められる
堅牢な
インターフェース
![Page 78: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/78.jpg)
ユニットテストの実装はプログラミング行為そのものである 製品コード・テストコード両方に対して、保守性を高めるパターンを適用し継続的に記述改善する必要がある
![Page 79: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/79.jpg)
ユニットテストの保守性作りこみアプローチ2
構造設計の工夫
![Page 80: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/80.jpg)
ユニットテストのコードはAssertionの羅列ではない テストコードも構造を持ち、それがユニットテストの保守性に影響を与えるため、構造設計の視点が必要になる
![Page 81: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/81.jpg)
内部設計
![Page 82: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/82.jpg)
テストのツリー構造
ユニットテスト
Test Suite
Test Case
Assertion
Assertion
Test Case
… Test Suite
…
![Page 83: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/83.jpg)
テストのツリー構造
ユニットテスト
Test Suite
Test Case
Assertion
Assertion
Test Case
… Test Suite
…
少数のAssertionでTestCaseを構成する (1条件/1テストケースなど
テスト設計の最小構成単位を反映)
細粒度・可読性
テスト設計やテストの意図が
分かりやすくなる
![Page 84: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/84.jpg)
テストのツリー構造
ユニットテスト
Test Suite
Test Case
Assertion
Assertion
Test Case
… Test Suite
…
独立・無干渉
独立・無干渉
独立性、自己完結性の確保
変更や流用の影響を局所化できる
![Page 85: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/85.jpg)
テストのツリー構造
ユニットテスト
Test Suit
Test Case
Assertion
Assertion
Test Case
… Test Suit
…
Test Suite:テスト対象Class = n : 1 ※Fixture/外部コンポーネント等で分離
通常は1:1
可読性など
構造に対する網羅を
チェックしやすくする
![Page 86: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/86.jpg)
ユニットテストの内部構造は一般的にツリー構造をとる そのため葉のコンパクト化、枝の独立性向上、適切な分割といった構造化設計の工夫が重要となる それによりモジュール性や可読性が向上し、ユニットテストの保守性が作りこまれる
![Page 87: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/87.jpg)
外部インターフェース設計
![Page 88: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/88.jpg)
外部インターフェース
• 外部コンポーネントは保守性を低下させるリスクになる
テスト
テスト対象
テスト
ブラックボックス
外部IF
![Page 89: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/89.jpg)
外部インターフェース
• 外部コンポーネントは保守性を低下させるリスクになる
テスト
テスト対象
テスト
ブラックボックス 状態を持つ
ブラックボックスを介してテストが結合
外部IF
![Page 90: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/90.jpg)
ユニットテストの保守性を損なう外部コンポーネントは、外部インターフェースの改善で分離する必要がある
外部コンポーネントを使用しないテスト
ブラックボックスを使用するテスト
StubやMockなど
テスト対象
ブラックボックス
![Page 91: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/91.jpg)
構造設計による改善
![Page 92: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/92.jpg)
ユニットテストの内部構造や外部インターフェースはその保守性に影響するため、構造設計・アーキテクチャ設計を俯瞰的に洗練する視点が要求される ただし留意点が2つある
![Page 93: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/93.jpg)
1:柔軟なズームアウト/ ズームインが必要
• ユニットテストを継続活用する場合、ユニットテストはしばしばボトムアップに実装されるため、構造設計は実装状況に応じて柔軟に行わなければならない
構造設計
実装
ズームアウト
ズームイン
![Page 94: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/94.jpg)
2:改善対象として製品コード/ テストコードを区別しない
• ユニットテストの障害は、製品コード/テストコード両方から除去する
• 両者のインターフェースを統合的に洗練させる
![Page 95: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/95.jpg)
テスト対象が
外部コンポーネントに依存している
void CameraCtrl::hoge()
{
_motorCtrl.run();
…
}
void CameraCtrl::fuga()
{
_motorCtrl.stop();
…
}
class CameraCtrl
実装
構造設計
外部コンポーネントを制御
![Page 96: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/96.jpg)
Test Suite
MotorCtrl (外部コンポーネント)
CameraCtrl
テストが 外部コンポーネントを
共有している
実装
構造設計
ズームアウト
![Page 97: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/97.jpg)
Test Suite
Test Suite
外部コンポーネント
CameraCtrl
テスタビリティの作りこみ
外部コンポーネントを 置換可能にする
実装
構造設計
MockやStub等
![Page 98: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/98.jpg)
Test Suite
Test Suite
MotorCtrl
CameraCtrl
Dependency Injection
void CameraCtrl::CameraCtrl()
{
_motorCtrl.open(...):
...
}
void CameraCtrl::CameraCtrl(MemoryCtrl memoryCtrl)
{
_motorCtrl = motorCtrl;
_motorCtrl.open(...):
...
}
実装
構造設計
ズームイン
![Page 99: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/99.jpg)
外部コンポーネントを用いるテスト
右以外のテスト
MotorCtrl
CameraCtrl
Mock、 Stub等
構造設計
実装
Dependency Injection
![Page 100: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/100.jpg)
ユニットテストのテストコードはAssertionのリストではない テストコードも構造を持ち、それがユニットテストの保守性に影響を与える 継続的な構造設計の改善で、テストの保守性を高めよう
![Page 101: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/101.jpg)
実装・構造設計の参考図書
• xUnit Test Patterns
– –Refactoring Test Code
• 読書会サイトに翻訳情報あり – <http://www.fieldnotes.jp/xutp/>
– 「xutp読書会」で検索
![Page 102: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/102.jpg)
ユニットテストの保守性作りこみアプローチ3
設計の工夫
![Page 103: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/103.jpg)
設計の工夫
1. テスト設計を洗練する
2. トップダウンのテスト設計で保守性を作りこむ
![Page 104: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/104.jpg)
テスト設計の洗練
![Page 105: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/105.jpg)
ユニットテストの継続的活用 においての留意点
• 作りすぎたテストを継続的に保守しようとすると保守性に悪影響を与える
TEST(…)
{
…
EXPECT_EQ(…, Hoge());
} void Hoge()
{
…
}
TEST(…)
{
…
EXPECT_EQ(…, Hoge());
} TEST(…)
{
…
EXPECT_EQ(…, Hoge());
}
製品コード
テストコード
製品コードを変更する
と大量のテストがビルドエラー
テスト設計を変更する
と修正が複数にまたがる
![Page 106: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/106.jpg)
保守性を高めるにはコンパクトなテストで十分な網羅性を確保する必要がある その実現にテスト設計技法やテスティングリテラシーの素養は有効である
![Page 107: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/107.jpg)
境界値分析
• 入出力値をグルーピング(例えば同値分割)して、グループの境界の値を抽出する
• 欠陥が境界付近に偏在するという経験則に立脚
![Page 108: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/108.jpg)
境界値分析
• 「6歳未満は無料。6歳以上12歳以下は半額。13以上は定額」
![Page 109: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/109.jpg)
境界値分析
• 「6歳未満は無料。6歳以上12歳以下は半額。13以上は定額」
-∞ +∞
ありえない
無料
半額
定額
0
-1
5
6 12
13
![Page 110: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/110.jpg)
カバレッジ分析
• 制御フローの網羅度を基準にテスト設計を行う
![Page 111: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/111.jpg)
start
入力:ループ回数
end
任意の回数
ループ
カバレッジ分析
![Page 112: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/112.jpg)
start
入力:ループ回数
end
任意の回数
ループ
カバレッジ分析
カバレッジレベル6(C6)
テストで用いるループ回数: 0回
1回
頻出する代表値
最大回
![Page 113: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/113.jpg)
テストの目的に応じて柔軟に 設計技法を使い分ける
テスト設計
仕様
コード構造
その他
![Page 114: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/114.jpg)
テストの目的に応じて柔軟に 設計技法を使い分ける
4で割り切れる N Y Y Y
100で割り切れる N N Y Y
400で割り切れる N N N Y
うるうどし N Y N Y
//うるう年か判定する
bool isLeapYear(unsigned int year)
{
if (year % 400 == 0)
{
return true;
}
if ((year % 4 == 0) && (year % 100 != 0))
{
return true;
}
return false;
}
テスト設計
仕様
コード構造
その他
機能的な保証
テストファースト
etc..
機械的なリファクタリング
テスタビリティの評価
etc..
![Page 115: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/115.jpg)
テストの目的に応じて柔軟に 設計技法や考え方を応用する
仕様が厳密なので仕様ベースのテスト設計ですまそう
任意の値でいいけど取り合えず境界値を
指定しよう
仕様が不明だけど単純な分岐構造なのでC3のテストを用意してリファクタリングしよう
TEST(…)
{
…
EXPECT_EQ(?, ?);
}
/**
* @brief …詳細な関数仕様…
*/
void fuga(…) {
}
bool hoge(unsigned int year)
{
if (year % 400 == 0)
{
return true;
}
if ((year % 4 == 0) && (year % 100 != 0))
{
return true;
}
return false;
}
![Page 116: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/116.jpg)
テスト設計の技法や考え方のレパートリーが増えればより洗練されたテストが書けるようになり、ユニットテストの保守性向上につながる
![Page 117: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/117.jpg)
テスト設計技法 参考図書
![Page 118: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/118.jpg)
2.トップダウンのテスト設計で 保守性を作りこむ
![Page 119: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/119.jpg)
テストコードの
構造設計
テストコードの
実装
テスト設計
テスト実装
テスト設計フロー テストコード実装フロー
ユニットテストの継続的活用におけるテスト設計フロー
テスト設計フロー
![Page 120: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/120.jpg)
テスト設計
テスト実装
テスト条件、テスト設計技法等を整理する
テストケースを求める、等
テストケースの選定を行う
テストに用いる具体値を求める、等
テスト設計フロー
![Page 121: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/121.jpg)
俯瞰的なテスト設計もユニットテストの保守性の作りこみにとって必須である テストコードの構造設計と同様、柔軟なズームアウト/ズームインで洗練されたテスト設計を実現しよう
![Page 122: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/122.jpg)
テストコードの
構造設計
テストコードの
実装
テスト設計
テスト実装
テスト設計フロー テストコード実装フロー
ユニットテストの継続的活用におけるテスト設計フロー
テスト条件の抽出による 外部インターフェースの洗練
![Page 123: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/123.jpg)
テストコードの
構造設計
テストコードの
実装
テスト設計
テスト設計プロセス テストコードの実装
●テスト条件リスト
・DB
・タッチパネル
・メカニカルボタン
・UART通信
….
通常テスト
DB
DB
テスト
DB
スタブ UART
UART
テスト
UART
スタブ …
…
ユニットテストの制約となる
外部コンポーネントを抽出し、構造設計に反映させる
1. テスト条件を抽出 2. トップダウンでインターフェース設計
テスト実装
全体整合のとれた外部インターフェースを構築
![Page 124: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/124.jpg)
テストコードの
構造設計
テストコードの
実装
テスト設計
テスト実装
テスト設計フロー テストコード実装フロー
ユニットテストの継続的活用におけるテスト設計フロー
テスト対象の安定度分析による ユニットテストの作りこみ
![Page 125: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/125.jpg)
テストコードの
構造設計
テストコードの
実装
テスト設計
テスト設計プロセス テストコードの実装 安定したプログラムユニットを抽出し、イテレーティブな
ユニットテストの作りこみに反映する
テストの品質
開発時間
ゴールとなる水準 ファイル 変更step/week
Hoge.cpp 0
Fuga.cpp 23
PIyo.cpp 133
… …
Hoge Fuga Piyo …
1 ○ ○
2 ○ ○
3
…
1. テスト対象の安定度評価 2. 変更リスクの少ないテストを作りこみ
テストの作りすぎによるデメリットを軽減する
テスト実装
![Page 126: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/126.jpg)
テスト計画/テストの上位設計の重要性
• 開発中継続的にユニットテストを構築していく場合
![Page 127: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/127.jpg)
開発中継続的にユニットテストを構築していく場合、テスト計画・テスト上位設計は特に重要である 望ましいテストは何か、いつどのようなテストを確保すべきか、どのようにテストを管理するかトップダウンで決めることで、柔軟かつ洗練されたテストを確保できる
![Page 128: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/128.jpg)
テスト計画・テスト上位設計
• テスト目的、テスト対象 • 望ましいテストの構造
– テストクラスの粒度 • Feature/Class/Fixture/Method/Story
– 望ましいAssertionの数 – テストスイートの粒度
• テストをどのように配置するか – テストレベル/テストタイプ/テストフェーズ/テストの目的
• 期待するテストの網羅性 – ソフトウェアのどこをテストするか
• コンポーネント/パッケージ/外部コンポーネント
– カバレッジはどの程度か • 構造的カバレッジ/機能的カバレッジ
![Page 129: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/129.jpg)
テスト計画・テスト上位設計
• テストの品質の管理・保証方法
– カバレッジやテストコード等の管理
• どう維持するか/いつどのように保証するか
• テスト環境の整備
– Test Doubleの開発管理
– ツールや環境の管理
• フェーズや計画
– どの段階でどの程度のテストを確保するか
![Page 130: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/130.jpg)
テストコードと同じく、テスト設計も上位構造を持つ 保守性の障害となるテスト条件や制約、テスト設計の抽象構造を抽出し、下位の設計に反映させていこう
![Page 131: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/131.jpg)
まとめ
![Page 132: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/132.jpg)
ユニットテストの実装はプログラミング行為そのもの 保守性を高めるパターンを適用し、継続的に記述改善しよう
![Page 133: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/133.jpg)
テストコードはAssertの羅列ではない 保守性に優れたアーキテクチャ・構造をテストコードに持たせよう また構造のズームアウト/ズームインを柔軟に行って、継続的にテストコードの設計を改善させていこう
![Page 134: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/134.jpg)
テスト設計はテストコードの保守性を左右する 保守性に優れたテストを書くためテスト設計技法、リテラシーをどんどん身につけよう テスト設計でもトップダウンでテスト設計の改善をすすめよう テスト計画・上位設計でより良いテストを効率的に確保しよう
![Page 135: ユニットテストの保守性を作りこむ, xpjugkansai2011](https://reader034.vdocuments.mx/reader034/viewer/2022052523/5561a273d8b42ae1538b4ae7/html5/thumbnails/135.jpg)
ご清聴ありがとうございました