å質ä¿èšŒç¹éïŒGoogleTestïŒgtestïŒãšTDDïŒãã¹ãé§åéçºïŒå®è·µå
¥é
ãåãããŸã§ãã°ãåãããªããããã®è±åŽïŒCI/CDã§å£ããªãå
ç¢ãªC++èšèšãšãã¹ãææ³
C++çŸå Žã§å¿
é ãšããããŠããããã¹ããã¬ãŒã ã¯ãŒã¯ãGoogleTestïŒgtestïŒãã®å°å
¥ã»èšè¿°æ³ãšããã¹ãé§åéçºïŒTDDïŒã®Red-Green-Refactorãµã€ã¯ã«ã培åºè§£èª¬ããã¹ãå®¹ææ§ã®é«ãççµåèšèšïŒDI: äŸåæ§æ³šå
¥ïŒã®çé«ãåŠã³ãŸãã
4.1 ãªãC++ãããŠããããã¹ããšèªåãã¹ããäžå¯æ¬ ãªã®ãïŒ
æåãã¹ãé Œã¿ã®C++éçºã¯ç Žæ»
ãæããŸããã¡ã¢ãªç Žå£ã»æªå®çŸ©åäœã»ãªãã¡ã¯ã¿ãªã³ã°ã®ææã«ç«ã¡åããå¯äžã®æŠåšãèªåãã¹ãã§ãã
ð§ ãã³ã®ã³å
茩
ã·ãã¯ãæå°å®âŠâŠïŒã¡ãã£ãšåœããå€å®ã®èšç®åŒã綺éºã«çŽããã ããªã®ã«ããªããæµã®HPããã€ãã¹ã«ãªã£ãŠãæ»ãªãªããªã£ãŠãã²ãŒã ãã¯ã©ãã·ã¥ããŠããŸããŸããâŠâŠãæåã§äœåãã²ãŒã ããã¬ã€ããŠç¢ºèªããã®ãæ¬åœã«éçã§ãâŠâŠïŒ
ð»ââïž ã·ãã¯ãå
ç
ãããçãã»ã©åããããC++ã¯PythonãJavaã®ãããªå®è¡æäŸå€ã¬ãŒããèããäºçްãªãã°ããã¡ã¢ãªç Žå£ãããæªå®çŸ©åäœããšããææªã®åœ¢ã§å¹ãé£ã¶ãã ãããããããã®çŸå Žã§ã¯ãæåãã¬ã€ã§ã®åäœç¢ºèªãã§ã¯ãªããããªç§åäœã§äœååãèµ°ãèªåãŠããããã¹ããããã«ããã€ãã©ã€ã³ïŒCIïŒã«çµã¿èŸŒãã®ã絶察垞èãªã®ããïŒ
### æåãã¹ã vs èªåãŠããããã¹ãã®æ±ºå®çãªéã
| é
ç® | æåãã¹ãïŒãããã°ãã¬ã€ïŒ | èªåãŠããããã¹ãïŒGoogleTestïŒ |
| :--- | :--- | :--- |
| **å®è¡é床** | 1åãããæ°åãæ°åå | **1000ä»¶ã§æ°ç§ãããªç§åäœ** |
| **ç¶²çŸ
æ§** | 人éã®æ³šæåã«äŸåïŒèŠèœãšãå€çºïŒ | **å¢çå€ã»ç°åžžç³»ã100%æ©æ¢°çã«æ€èšŒ** |
| **ãªãã¡ã¯ã¿èæ§** | ãåããŠããã³ãŒãã¯æããŠè§Šããªãã | **ãã¹ããéãã°ãå£ããŠããªãããšå³åº§ã«ç¢ºä¿¡** |
| **CI/CD飿º** | äžå¯èœïŒäººéã®ç®èŠãå¿
èŠïŒ | **Gitããã·ã¥æã«GitHub Actionsçã§èªåå®è¡** |
### C++ã«ãããããã¹ãå®¹ææ§ïŒTestabilityïŒããèšèšãçŸãããã
ãã¹ããæžãã«ããã³ãŒãã¯ãäŸå€ãªã**ãå¯çµåïŒSpaghetti CodeïŒã**ã§ãã
- ã°ããŒãã«å€æ°ã«äŸåããŠãããšããã¹ãããšã«ç¶æ
ããªã»ããã§ããªãã
- ä»ã®ã¯ã©ã¹ã `new` ã§çŽçµããŠãããšããã¹ã察象ã ããåãé¢ããªãã
ãã¹ããæžãããšããããšèªäœããã¯ã©ã¹ããççµåããã€ãæç¢ºãªè²¬åããžãšåŒ·å¶çã«é²åãããæé«ã®èšèšæéã«ãªããŸãã
ð¡ ãã®ã»ã¯ã·ã§ã³ã®éèŠãã€ã³ã
- ãã¹ãã¯ãåŸä»ãã®æ€åãã§ã¯ãªããèšèšã®çŸ
éç€ã: ãã¹ããæžããããããã«é¢æ°ãã¯ã©ã¹ã®åŒæ°ãæŽããããšã§ãèªç¶ãšäŸåæ§ãäžããåå©çšæ§ã®é«ãã³ãŒããçãŸããŸãã
4.2 GoogleTestïŒgtestïŒã®åºæ¬ææ³ïŒASSERT vs EXPECT
äžçæšæºã®C++ãã¹ããã¬ãŒã ã¯ãŒã¯ GoogleTest ã®äžæ žãã¯ããšããã¹ãã±ãŒã¹ã®æžãæ¹ããã¹ã¿ãŒããŸãã
ð§ ãã³ã®ã³å
茩
GoogleTestã£ãŠãã©ããã£ãŠååŠãå€å®ãããã§ããïŒ `assert()` ã¿ãããªãã®ã§ããïŒ
ð»ââïž ã·ãã¯ãå
ç
ãã質åããïŒgtestã«ã¯äž»ã« `EXPECT_*` ãš `ASSERT_*` ã®2çš®é¡ã®ã¢ãµãŒã·ã§ã³ãããããã®2ã€ã®äœ¿ãåããããæåã®éèŠé¢éãããïŒ
### EXPECT_* ãš ASSERT_* ã®éã
1. **`EXPECT_EQ(val1, val2)` / `EXPECT_TRUE(cond)` (éèŽåœç倱æ)**:
- æ€èšŒã«å€±æããŠãã**ãã®ãã¹ã颿°ã®æ¬¡ã®è¡ä»¥éã®æ€èšŒãç¶è¡**ããŸãã
- 1ã€ã®ãã¹ãã±ãŒã¹å
ã§è€æ°ã®é
ç®ïŒX座æšãšY座æšã®äž¡æ¹ãªã©ïŒãäžåºŠã«ãã§ãã¯ãããå Žåã«æé©ã§ãã
2. **`ASSERT_EQ(val1, val2)` / `ASSERT_NE(ptr, nullptr)` (èŽåœç倱æ)**:
- æ€èšŒã«å€±æããå Žåã**ãã®ãã¹ã颿°ã®å®è¡ãå³åº§ã«äžæïŒabortïŒ**ããŸãã
- ãã€ã³ã¿ã `nullptr` ã§ãªãããšã確ãããŠããã¡ã³ãã¢ã¯ã»ã¹ããå Žåãªã©ã倱æãããŸãŸé²ããšã»ã°ãã©ãèµ·ããç®æã§äœ¿çšããŸãã
```cpp
#include <gtest/gtest.h>
// ãã¹ã察象ã®é¢æ°
int add(int a, int b) { return a + b; }
// TEST(ãã¹ãã°ã«ãŒãå, ãã¹ãå)
TEST(MathTest, AddPositiveNumbers) {
EXPECT_EQ(add(2, 3), 5);
EXPECT_EQ(add(0, 0), 0);
}
TEST(PointerTest, SafeDereference) {
int* ptr = getResource();
// ptrãnullãªãããã§å³æäžæïŒãã«ãã€ã³ã¿åç
§ã¯ã©ãã·ã¥ã鲿¢ïŒ
ASSERT_NE(ptr, nullptr);
EXPECT_EQ(*ptr, 42);
}
```
ð¡ ãã®ã»ã¯ã·ã§ã³ã®éèŠãã€ã³ã
- åºæ¬ã¯ EXPECT_*ããã€ã³ã¿åæã¯ ASSERT_*: å¯èœãªéãå€ãã®å€±ææ
å ±ãäžåºŠã«éããããã«æ®æ®µã¯ EXPECT_* ã䜿ããåŸç¶åŠçãã¯ã©ãã·ã¥ããå±éºãããå Žåã®ã¿ ASSERT_* ã䜿ããŸãã
4.3 TDDïŒãã¹ãé§åéçºïŒã®ç¥é«ïŒRed -> Green -> Refactor
å®è£
ã³ãŒãã1è¡ãæžãåã«ããŸãã倱æãããã¹ãããæžãïŒãã®é転ã®çºæ³ããã°ã®æ··å
¥ããŒãã«ããŸãã
ð§ ãã³ã®ã³å
茩
ãã£ïŒïŒããã°ã©ã ãæžãåã«ãã¹ããæžããã§ããïŒïŒãŸã 颿°ã®äžèº«ãååšããªãã®ã«ãã©ããã£ãŠãã¹ããåãããã§ããïŒïŒ
ð»ââïž ã·ãã¯ãå
ç
ã«ãã«ãã«ïŒãããããœãªãããïŒ
ãã¹ããå
ã«æžããšããããšã¯ãããã®é¢æ°ã¯ã©ãåŒã°ããããïŒã©ããªåŒæ°ã§äœãè¿ãã¹ããïŒããšãããå©çšè
ã®èŠç¹ïŒAPI仿§ïŒãã匷å¶çã«åºããäœæ¥ãªãããã仿§ã決ãŸã£ãŠããäžèº«ãäœãããããTDDã®çé«ããïŒ
### TDDã®3ã¹ããããµã€ã¯ã«
```text
âââ⺠[1. Red] 倱æãããã¹ããæžã
â ââ 仿§ïŒAPIïŒã決ãããã¹ããæ£ãããèµ€è²ïŒå€±æïŒãã«ãªãããšã確èª
â â
â âŒ
â [2. Green] æçã§ãã¹ããéãã³ãŒããæžã
â ââ ãã¿æžãã»ããŒãã³ãŒãã§ãOKïŒãŸãã¯ãã¹ãããç·è²ïŒæåïŒãã«å
ããã
â â
â âŒ
â [3. Refactor] ãã¹ããéã£ãç¶æ
ãç¶æããŠã³ãŒããæŽçãã
â ââ éè€æé€ã»åœåæ¹åã»æé©åïŒèªåãã¹ããããããæããªãïŒïŒ
â â
âââââââââââââ (æ¬¡ã®æ©èœã»ä»æ§ãž / åã³ Red ãããµã€ã¯ã«ãåã)
```
#### ã€ã³ããŒããŒè¡çªå€å®ã®å®äŸ
```cpp
// STEP 1: RedïŒãŸã checkCollision ã®äžèº«ã¯ç©ºã£ãœïŒ
TEST(CollisionTest, BulletHitsInvader) {
Bullet bullet(10, 5);
Invader invader(10, 5);
// åŒŸãšæµãå座æšãªã true ãè¿ãã¹ããšãã仿§ãã¹ã
EXPECT_TRUE(checkCollision(bullet, invader));
}
// STEP 2: GreenïŒãã¹ããéãæå°éã®å®è£
ïŒ
bool checkCollision(const Bullet& b, const Invader& i) {
return b.getX() == i.getX() && b.getY() == i.getY();
}
// STEP 3: RefactorïŒãµã€ãºå€å®ãå¢çæ¡ä»¶ã®è¿œå ã»æ§é åïŒ
```
ð¡ ãã®ã»ã¯ã·ã§ã³ã®éèŠãã€ã³ã
- ããã¹ãããã¹ããç¶æ
ããšãã絶察çã»ãŒããã£ããã: ãã¹ãã³ãŒããããããããã倧èãªèšèšæ¹åïŒãªãã¡ã¯ã¿ãªã³ã°ïŒãè¡ã£ãŠãæ¢åã®æ©èœãå£ããŠããªãããšãæ°ç§ã§èšŒæã§ããŸãã
4.4 ãã¹ãå®¹ææ§ãçã¿åºããäŸåæ§æ³šå
¥ïŒDIïŒããã¿ãŒã³
ããŒããŠã§ã¢ãç»é¢æç»ãæéã«çŽçµããã³ãŒãã¯ãã¹ãã§ããŸãããã€ã³ã¿ãŒãã§ãŒã¹ãæãã§å€ããæ³šå
¥ããèšèšææ³ãåŠã³ãŸãã
ð ISoundEngine.h
#pragma once
// ãµãŠã³ãæ©èœã®æœè±¡ã€ã³ã¿ãŒãã§ãŒã¹
class ISoundEngine {
public:
virtual ~ISoundEngine() = default;
virtual void playExplosion() = 0;
virtual void playLaser() = 0;
};
ð PlayerWithSound.h
#pragma once
#include "ISoundEngine.h"
class PlayerWithSound {
private:
int m_x;
int m_y;
ISoundEngine& m_sound; // å
·è±¡ã§ã¯ãªãã€ã³ã¿ãŒãã§ãŒã¹ã®åç
§ãä¿æïŒDIïŒ
public:
PlayerWithSound(int x, int y, ISoundEngine& sound)
: m_x(x), m_y(y), m_sound(sound) {}
void shoot() {
// çºå°åŠçãè¡ããé³ã鳎ãã
m_sound.playLaser();
}
};
ð PlayerTest.cpp
#include <gtest/gtest.h>
#include "PlayerWithSound.h"
// ãã¹ãçšã®ãããŒé³é¿ãšã³ãžã³ïŒã¢ãã¯ïŒ
class MockSoundEngine : public ISoundEngine {
public:
int laserPlayCount = 0;
int explosionPlayCount = 0;
void playLaser() override {
laserPlayCount++;
}
void playExplosion() override {
explosionPlayCount++;
}
};
// èªæ©ã®çºå°æã«ã¬ãŒã¶ãŒé³ãæ£ç¢ºã«1åèŠæ±ãããããæ€èšŒãããŠããããã¹ã
TEST(PlayerTest, ShootPlaysLaserSoundExactlyOnce) {
MockSoundEngine mockSound;
PlayerWithSound player(10, 20, mockSound);
// ãã¹ãåã¯åçåæ°0
EXPECT_EQ(mockSound.laserPlayCount, 0);
// çºå°ãå®è¡
player.shoot();
// æ£ç¢ºã«1ååŒã°ããããšãæ€èšŒïŒæ¬ç©ã®ã¹ããŒã«ãŒããªããŠããã¹ãå®çµïŒ
EXPECT_EQ(mockSound.laserPlayCount, 1);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
ð¡ ãã®ã»ã¯ã·ã§ã³ã®éèŠãã€ã³ã
- å
·è±¡ã¯ã©ã¹ã«çŽæ¥äŸåãããã€ã³ã¿ãŒãã§ãŒã¹ãåç
§ãã: ã¹ããŒã«ãŒããã¡ã€ã«IOããããã¯ãŒã¯ãªã©ã®å€éšãªãœãŒã¹ã¯ã€ã³ã¿ãŒãã§ãŒã¹åããŠå€ããæž¡ãïŒDIïŒããšã§ããã¹ãæã«100%å¶åŸ¡å¯èœãªã¢ãã¯ã«å·®ãæ¿ããããŸãã
ð ç解床ãã§ãã¯ã¯ã€ãº
Q1. GoogleTestã«ãããŠãEXPECT_EQ ãš ASSERT_EQ ã®æ±ºå®çãªåäœã®éãã¯äœã§ãããïŒ
- 1. EXPECT_EQ ã¯æŽæ°å°çšã§ãASSERT_EQ ã¯æµ®åå°æ°ç¹å°çšã§ããããš
- 2. EXPECT_EQ ã¯æ€èšŒå€±æåŸããã¹ã颿°å
ã®åŸç¶åŠçãç¶è¡ããããASSERT_EQ ã¯å€±æããæç¹ã§ãã®ãã¹ã颿°ã®å®è¡ãçŽã¡ã«äžæããããš
- 3. ASSERT_EQ ã¯äŸå€ãã¹ããŒããããEXPECT_EQ ã¯ããã°ã©ã å
šäœãå³åº§ã«ã¯ã©ãã·ã¥ãããããš
- 4. EXPECT_EQ ã¯ã³ã³ãã€ã«æã®ã¿æ€èšŒãããå®è¡æã«ã¯ç¡èŠãããããš
æ£è§£ãšè§£èª¬ãèŠã
æ£è§£ïŒEXPECT_EQ ã¯æ€èšŒå€±æåŸããã¹ã颿°å
ã®åŸç¶åŠçãç¶è¡ããããASSERT_EQ ã¯å€±æããæç¹ã§ãã®ãã¹ã颿°ã®å®è¡ãçŽã¡ã«äžæããããš
æ£è§£ã§ãïŒEXPECT_* ã¯ãã¹ã倱æãèšé²ãã€ã€æ¬¡ã®æ€èšŒãžé²ããããäžåºŠã«å€ãã®äžå
·åç®æãææ¡ã§ããŸããäžæ¹ ASSERT_* ã¯çŽã¡ã«äžæããããããã€ã³ã¿ãéãã«ã§ããåæã§åŸç¶ã®æ€èšŒãè¡ãå Žåãªã©ã«çšããããŸãã
Q2. TDDïŒãã¹ãé§åéçºïŒã®åºæ¬ãµã€ã¯ã«ãRed -> Green -> Refactorãã®æ£ããé åºãšèª¬æã¯ã©ãã§ãããïŒ
- 1. ãªãã¡ã¯ã¿ãªã³ã°ãããŠããããã¹ããæžããæåŸã«å®è£
ããããš
- 2. ãŸã倱æãããã¹ãïŒRedïŒãæžããæçã§ãã¹ããééãããã³ãŒãïŒGreenïŒãæžãããã¹ããéã£ãç¶æ
ãç¶æãããŸãŸã³ãŒãã綺éºã«æ¹åïŒRefactorïŒããããš
- 3. ã³ãŒããå
šéšå®æãããŠãããèªåãã¹ããèµ°ãããŠç·è²ã«ãªããŸã§ä¿®æ£ããããš
- 4. ãã¹ãŠã®å€æ°ã private ã«ããŠãããpublic 颿°ã远å ããããš
æ£è§£ãšè§£èª¬ãèŠã
æ£è§£ïŒãŸã倱æãããã¹ãïŒRedïŒãæžããæçã§ãã¹ããééãããã³ãŒãïŒGreenïŒãæžãããã¹ããéã£ãç¶æ
ãç¶æãããŸãŸã³ãŒãã綺éºã«æ¹åïŒRefactorïŒããããš
æ£è§£ã§ãïŒãã¹ããå
ã«æžãããšã§ãããã¹ãå€éšä»æ§ããåºããGreenã§æçéæããæåŸã«ãã¹ããšããå®å
šç¶±ãããç¶æ
ã§ã³ãŒãã®å質ãé«ããïŒRefactorïŒã®ãTDDã®éåã§ãã
Q3. ã¹ããŒã«ãŒããé³ã鳎ããåŠçããŠããããã¹ãã§å®å
šãã€èªåçã«æ€èšŒããããã«æãæå¹ãªèšèšææ³ã¯ã©ãã§ãããïŒ
- 1. ãã€ã¯ãPCã«è¿ã¥ããŠããã¹ãå®è¡æã«é³ãé²é³ããŠé³éã調ã¹ã
- 2. é³å£°ã鳎ããå
·è±¡ã¯ã©ã¹ãžã®çŽæ¥äŸåããããISoundEngine ãªã©ã®ã€ã³ã¿ãŒãã§ãŒã¹ãå°å
¥ããŠå€éšããæ³šå
¥ïŒäŸåæ§æ³šå
¥: DIïŒãããã¹ãæã¯åŒåºåæ°ãèšé²ããã¢ãã¯ã¯ã©ã¹ã«å·®ãæ¿ãã
- 3. ãã¹ãäžã¯ãµãŠã³ãæ©èœã #ifdef ã§ã³ã¡ã³ãã¢ãŠãããŠäžååŒã°ãªãããã«ãã
- 4. ã°ããŒãã«å€æ°ã«é³å£°ãã¡ã€ã«ãã¹ãä¿åããŠãã
æ£è§£ãšè§£èª¬ãèŠã
æ£è§£ïŒé³å£°ã鳎ããå
·è±¡ã¯ã©ã¹ãžã®çŽæ¥äŸåããããISoundEngine ãªã©ã®ã€ã³ã¿ãŒãã§ãŒã¹ãå°å
¥ããŠå€éšããæ³šå
¥ïŒäŸåæ§æ³šå
¥: DIïŒãããã¹ãæã¯åŒåºåæ°ãèšé²ããã¢ãã¯ã¯ã©ã¹ã«å·®ãæ¿ãã
æ£è§£ã§ãïŒäŸåæ§æ³šå
¥ïŒDI: Dependency InjectionïŒã«ããå€éšã€ã³ã¿ãŒãã§ãŒã¹ãå·®ãæ¿ãå¯èœã«ããããšã§ã宿©ã®ããŒããŠã§ã¢ãç°å¢ã«äŸåãããçŽç²ãªC++ããžãã¯ãšããŠããªç§ã§ãã¹ãå¯èœã«ãªããŸãã
Q4. ããŠããããã¹ããæžããããã³ãŒãããæã€äžè¬çãªç¹åŸŽãšããŠæ£ãããã®ã¯ã©ãã§ãããïŒ
- 1. ã°ããŒãã«å€æ°ãéç倿°ãå€çšãããŠãããã©ãããã§ãå€ãååŸã§ãã
- 2. 1ã€ã®é¢æ°ã«ãã¹ãŠã®ã¢ã«ãŽãªãºã ãéçŽãããŠããŠ1000è¡ä»¥äžãã
- 3. åã¯ã©ã¹ã®è²¬åãåäžïŒSRPïŒã§ãå€éšäŸåãå°ãªãççµåã«ä¿ãããŠãã
- 4. ã¯ã©ã¹ã®ç¶æ¿ã10éå±€ä»¥äžæ·±ããã¹ãããŠãã
æ£è§£ãšè§£èª¬ãèŠã
æ£è§£ïŒåã¯ã©ã¹ã®è²¬åãåäžïŒSRPïŒã§ãå€éšäŸåãå°ãªãççµåã«ä¿ãããŠãã
æ£è§£ã§ãïŒãã¹ããæžããããã³ãŒãã¯ã責åãåäžãã§ãççµåãã§ãããã¹ããæžãç¿æ
£ãæã€ããšèªäœããèªç¶ãšç¶ºéºãªãªããžã§ã¯ãæåèšèšãžãšå°ããŠãããŸãã
ð»ââïž ã·ãã¯ãC++ã©ã ã€ã³ã¿ã©ã¯ãã£ãåŠç¿ã·ã¹ãã
ãã©ãŠã¶ã§JavaScriptãæå¹ã«ãããšããã©ãŠã¶å
ã€ã³ããŒããŒã²ãŒã ãšãã¥ã¬ãŒã¿ãã¡ã¢ãªãããå¯èŠåãUMLã¯ã©ã¹å³ã察話å解説ãç解床ã¯ã€ãºãèµ·åããŸãã
ã€ã³ã¿ã©ã¯ãã£ãåŠç¿ãéå§ãã â