ãšããžã±ãŒã¹ãæé»çèŠä»¶ããããŠåé¡ãçºçããåã«ãããæŽé²ããé²åŸ¡çãã¹ãæŠç¥ãæããã«ãããç¶¿å¯ãªå®è·µè åãã¬ã€ãã
æ¢åµã®ãã€ã³ãã»ãã:äœãééãå¯èœæ§ãããã®ã?
TDDå®è·µè ã§ãããèªç§°ãšããžã±ãŒã¹æ¢åµã§ããç§ã¯ããããããŒãã¹ãã峿 Œã«ãã¹ãããªãããçŸå®äžçã®æ··æ²ãæœã圱ãå®å šã«ç¡èŠãããã¹ãã¹ã€ãŒããééããç¡æ°ã®ãã°ãèŠãŠããŸãããäžéœåãªçå®ãããã«ãããŸã: ããªãã®ãŠãŒã¶ãŒã¯ä»æ§ã«åŸããŸãã ã圌ãã¯ååãã£ãŒã«ãã«çµµæåãå ¥åããnullå€ã§ãã©ãŒã ãéä¿¡ããã³ã¡ã³ãããã¯ã¹ã«å°èª¬å šäœã貌ãä»ããã©ãããããã3ç§éã«ãéä¿¡ããã¿ã³ã17åã¯ãªãã¯ããããšã«æåããŸãã
åé¡ã¯äœããééããã©ããã§ã¯ãªããäœãééãããã€ééãããããŠããªãã®ãã¹ãããããæåã«æãããã©ããã§ãã
ãã®ã¬ã€ãã¯ãããå€ãã®ãã¹ããæžãããšã«ã€ããŠã§ã¯ãããŸãããå·é ·ãªäºä»¶ã解決ããæ¢åµã®ç¶¿å¯ãªç²ŸåºŠã§ãšããžã±ãŒã¹ã远ãè©°ããããè³¢ããã¹ããæžãããšã«ã€ããŠã§ããé²åŸ¡çããã°ã©ãã³ã°ã®ã¬ã³ãºãéããŠTDDãµã€ã¯ã«ãæ¢æ±ãããšããžã±ãŒã¹ãå®è¡å¯èœãªå顿³ã«ã«ããŽãªãŒåããã¹ããŒã¯ãã«ããŒãèšåãå¿ããæé»çèŠä»¶ãæããã«ãã倱æãç¡èŠããããšãäžå¯èœãªãã¹ããæ§é åããŸãã
Red-Green-Refactorãµã€ã¯ã«:å®è£ åã®ãã¹ã
ãšããžã±ãŒã¹ã远ãåã«ãåºç€ã確ç«ããå¿ èŠããããŸã:Test-Driven Development (TDD)ãKent Beckã®ç»æçãªTDDã«é¢ããç ç©¶ã¯ãã·ã³ãã«ãªããæ·±é ãªååã確ç«ããŸãã:æåã«ãã¹ããæžããããã倱æããã®ãèŠãŠ(Red)ãæå°éã®ã³ãŒãã§ãããééãã(Green)ããã®åŸãªãã¡ã¯ã¿ãªã³ã°ããŸã(Refactor)ã
ãªãæåã«ãã¹ããæžãã®ã?
å®è£ åŸã«ãã¹ããæžãããšã¯ãäŸµå ¥åŸã«ã»ãã¥ãªãã£ã·ã¹ãã ãã€ã³ã¹ããŒã«ãããããªãã®ã§ããäœãååšãã¹ãããå®çŸ©ããã®ã§ã¯ãªãããã§ã«ååšãããã®ãæ€èšŒããŠããã®ã§ããMartin Fowlerãæç¢ºã«è¿°ã¹ãŠããããã«ãTDDã¯ããã¹ããæžãããšã§ãœãããŠã§ã¢éçºãã¬ã€ããããâãã¹ãã¯ä»æ§ãã»ãŒããã£ãããããããŠèšèšããŒã«ã«ãªããŸãã
TDDãµã€ã¯ã«ã¯æ¬¡ã®ããã«ãªããŸã:
1. RED: æãŸããåäœãå®çŸ©ãã倱æãããã¹ããæžã
2. GREEN: ãã¹ããééãããæå°éã®ã³ãŒããæžã
3. REFACTOR: åäœãå€ããã«ã³ãŒãåè³ªãæ¹åãã
4. REPEAT: 次ã®ãã¹ãã±ãŒã¹ã«ç¶ãã
ãšããžã±ãŒã¹ãã³ã¿ãŒã®TDDã¯ãŒã¯ãããŒ
ããã§æšæºçãªTDDå®è·µããéžè±ããŸããã»ãšãã©ã®éçºè ã¯1ã€ã®ããããŒãã¹ãã¹ããæžãããããã°ãªãŒã³ã«ããŠãå ã«é²ã¿ãŸãããšããžã±ãŒã¹ãã³ã¿ãŒã¯ç°ãªãèãæ¹ãããŸã:
- RED: æåã«ããããŒãã¹ãã¹ããæžã(倱æããã¯ãã§ã)
- RED: å®è£ åã«ãšããžã±ãŒã¹ãã¹ããæžã(ãã¹ãŠå€±æããã¯ãã§ã)
- GREEN: ãã¹ãŠã®ãã¹ããåæã«æºããããã«å®è£ ãã
- REFACTOR: ãšããžã±ãŒã¹ãã«ããŒãããŠãããšããèªä¿¡ãæã£ãŠã¯ãªãŒã³ã¢ãããã
ãã®ã¢ãããŒãã¯ãæ¬çªã³ãŒããæžãåã«é²åŸ¡çã«èããããšã匷å¶ããŸããæ¢åã®å®è£ ã«ãã¹ããã¬ãããã£ããããã®ã§ã¯ãªããå®å šãªåäœå¥çŽãäºåã«å®çŸ©ããŠããã®ã§ãã
å ·äœäŸ:ã¡ãŒã«ããªããŒã·ã§ã³
äžèŠã·ã³ãã«ãªèŠä»¶ã§ãããå®éã«èŠãŠã¿ãŸããã:ãã¡ãŒã«ã¢ãã¬ã¹ãæ€èšŒãããã
// Step 1 & 2: 倱æãããã¹ããæžã (REDãã§ãŒãº)
describe('EmailValidator', () => {
let validator: EmailValidator;
beforeEach(() => {
validator = new EmailValidator();
});
// ããããŒãã¹ãã¹ã
it('should accept valid standard email format', () => {
expect(validator.isValid('user@example.com')).toBe(true);
});
// ãšããžã±ãŒã¹ãã¹ã - å®è£
åã«æžããã
it('should reject email without @ symbol', () => {
expect(validator.isValid('userexample.com')).toBe(false);
});
it('should reject email with multiple @ symbols', () => {
expect(validator.isValid('user@@example.com')).toBe(false);
});
it('should reject null or undefined input', () => {
expect(validator.isValid(null)).toBe(false);
expect(validator.isValid(undefined)).toBe(false);
});
it('should reject empty string', () => {
expect(validator.isValid('')).toBe(false);
});
it('should reject whitespace-only input', () => {
expect(validator.isValid(' ')).toBe(false);
});
it('should handle extremely long email addresses', () => {
const longLocal = 'a'.repeat(65) + '@example.com'; // ããŒã«ã«éšå > 64æå
expect(validator.isValid(longLocal)).toBe(false);
});
it('should reject email with special characters in wrong positions', () => {
expect(validator.isValid('.user@example.com')).toBe(false); // ãããã§å§ãŸã
expect(validator.isValid('user.@example.com')).toBe(false); // ãããã§çµãã
});
it('should accept plus addressing (valid RFC 5322)', () => {
expect(validator.isValid('user+tag@example.com')).toBe(true);
});
it('should handle international domain names correctly', () => {
expect(validator.isValid('user@mÃŒnchen.de')).toBe(true);
});
});
ããã§äœãèµ·ãã£ããæ³šç®ããŠãã ãã:æ¬çªã³ãŒãã1è¡ãå®è£ ããåã«9ã€ã®ãšããžã±ãŒã¹ãã¹ããæžããŸãããåãã¹ãã¯è³ªåã衚ããŠããŸã:ãäœãééãå¯èœæ§ãããã?ããããå®éã®æ¢åµã®ãã€ã³ãã»ããã§ãã
ãšããžã±ãŒã¹å顿³:æ··æ²ã®ã«ããŽãªãŒ
ãèµ·ããã¯ãããªãã£ããæ¬çªã€ã³ã·ãã³ãããããã°ããŠããé·å¹Žã®çµéšãéããŠããœãããŠã§ã¢ã®åŒ±ç¹ãäžè²«ããŠæŽé²ãããšããžã±ãŒã¹ã®å顿³ãéçºããŸããããããã®ã«ããŽãªãŒãçè§£ããããšã§ããšããžã±ãŒã¹ãã¹ããã©ã³ãã ãªåŠæ³ããäœç³»çãªèª¿æ»ã«å€ããŸãã
5ã€ã®äž»èŠã«ããŽãªãŒ:
- å¢çã±ãŒã¹ - MIN/MAXå€ãæååé·ãæ¥ä»ç¯å²ãé åã€ã³ããã¯ã¹
- Null/空ã±ãŒã¹ - nullãundefinedã空æååã空ã³ã¬ã¯ã·ã§ã³
- ãã©ãŒãããã±ãŒã¹ - ç¹æ®æå(SQL/XSS)ãUnicode/çµµæåãäžæ£ãªããŒã¿
- ç¶æ ã±ãŒã¹ - ã¬ãŒã¹ã³ã³ãã£ã·ã§ã³ãç¡å¹ãªé·ç§»ãã¿ã€ã ã¢ãŠã
- ãªãœãŒã¹ã±ãŒã¹ - ã¡ã¢ãªå¶éããããã¯ãŒã¯ã¿ã€ã ã¢ãŠããã¯ã©ãŒã¿è¶ é
1. å¢çå€ã±ãŒã¹
Boundary Value Analysis (BVA)ã¯ãå ¥åç¯å²ã®ç«¯ã§ã®åäœã調ã¹ãåºæ¬çãªãã¹ãææ³ã§ããååã¯ã·ã³ãã«ã§ã: ãšã©ãŒã¯å¢çã«éãŸããŸã ã50é ç®ãæ£ããåŠçãããœãããŠã§ã¢ã¯ã0é ç®ã1é ç®ããŸãã¯1,000,000é ç®ã§å£æ» çã«å€±æããå¯èœæ§ããããŸãã
ãã¹ãããå¢çã«ããŽãªãŒ:
- æ°å€å¢ç: ãŒããè² ã®æ°ãæå€§/æå°å€(INT_MAXãINT_MIN)
- æååå¢ç: 空æååãåäžæåãæå€§é·å¶é
- ã³ã¬ã¯ã·ã§ã³å¢ç: 空é åãåäžèŠçŽ é åã容éã«éããã³ã¬ã¯ã·ã§ã³
- æ¥ä»/æå»å¢ç: ãšããã¯ã¿ã€ã ãé幎ããµããŒã¿ã€ã é·ç§»ãã¿ã€ã ãŸãŒã³ã®ç«¯
- ã€ã³ããã¯ã¹å¢ç: æåã®èŠçŽ (0)ãæåŸã®èŠçŽ (length-1)ãç¯å²å€(-1ãlength)
// äŸ: ããŒãžããŒã·ã§ã³é¢æ°ã®ãã¹ã
public class PaginationTests {
private PageService pageService;
@Before
public void setUp() {
pageService = new PageService();
}
@Test
public void shouldHandleFirstPage() {
Page result = pageService.getPage(1, 10); // æåã®ããŒãž
assertNotNull(result);
assertEquals(1, result.getPageNumber());
}
@Test
public void shouldHandleZeroPageNumber() {
// å¢ç: ç¡å¹ãªäžé
assertThrows(IllegalArgumentException.class, () -> {
pageService.getPage(0, 10);
});
}
@Test
public void shouldHandleNegativePageNumber() {
// å¢ç: æå¹ç¯å²ä»¥äž
assertThrows(IllegalArgumentException.class, () -> {
pageService.getPage(-1, 10);
});
}
@Test
public void shouldHandleZeroPageSize() {
// å¢ç: ç¡å¹ãªããŒãžãµã€ãº
assertThrows(IllegalArgumentException.class, () -> {
pageService.getPage(1, 0);
});
}
@Test
public void shouldHandleMaximumPageSize() {
// å¢ç: äžéã®åŒ·å¶
Page result = pageService.getPage(1, 1000); // æå€§å€ã100ãšä»®å®
assertEquals(100, result.getPageSize()); // æå€§å€ã«ã¯ã©ã³ããããã¹ã
}
@Test
public void shouldHandlePageBeyondAvailableData() {
// å¢ç: ããŒãžçªå·ãç·ããŒãžæ°ãè¶
ãã
Page result = pageService.getPage(9999, 10);
assertTrue(result.getItems().isEmpty());
assertEquals(9999, result.getPageNumber());
}
@Test
public void shouldHandleSingleItemCollection() {
// å¢ç: æå°ã®æå³ã®ããããŒã¿
List<String> items = Arrays.asList("single-item");
Page result = pageService.paginate(items, 1, 10);
assertEquals(1, result.getTotalItems());
assertEquals(1, result.getTotalPages());
}
}
2. NullãUndefinedã空å€ã±ãŒã¹
10åãã«ã®éã¡ânullåç §âã¯ãæ¬ åŠã«å¯Ÿãããã¹ããäžè²«ããŠæ ã£ãŠããããããœãããŠã§ã¢ãèŠããç¶ããŠããŸãããã¹ãŠã®å ¥åãã©ã¡ãŒã¿ããã¹ãŠã®æ»ãå€ããã¹ãŠã®ã³ã¬ã¯ã·ã§ã³ã¯ãæœåšçã«nullãundefinedããŸãã¯ç©ºã§ããå¯èœæ§ããããŸãã é²åŸ¡çããã°ã©ãã³ã°ã¯ãããã3ã€ã®ç¶æ ãã¹ãŠãåŠçããããšãèŠæ±ããŸãã
Null/空ã«ããŽãªãŒ:
- Nullå€: æç€ºçãªnullåç §
- Undefinedå€: åæåãããŠããªã倿°(JavaScript/TypeScript)
-
空æåå:
""
vsnull
vsundefined
-
空ã³ã¬ã¯ã·ã§ã³:
[]
ã{}
ã空ã®map/set - Optional/Maybeå: åå®å šãªã©ãããŒã§ã®å€ã®æ¬ åŠ
3. ç¹æ®æåãšãã©ãŒãããæ€èšŒ
ãŠãŒã¶ãŒã¯ããã¹ããã£ãŒã«ãã«äœã§ãå ¥åããŸã:SQLã€ã³ãžã§ã¯ã·ã§ã³è©Šè¡ãXSSãã€ããŒããçµµæåãUnicodeå¶åŸ¡æåãããã³äžæ£ãªããŒã¿ããã©ãŒãããæ€èšŒã¯æ£ããã ãã§ãªãã ã»ãã¥ãªãã£ãšããŒã¿æŽåæ§ ã«ã€ããŠã§ãã
ç¹æ®æåã«ããŽãªãŒ:
-
SQLç¹æ®æå:
'
ã--
ã;
ãOR 1=1
-
HTML/JavaScript:
<script>
ã&
ã<
ã>
-
ãã¹ãã©ããŒãµã«:
../
ã..\\
ã絶察ãã¹ - Unicodeãšããžã±ãŒã¹: çµµæå(ãã«ããã€ã)ãå³ããå·ŠããŒã¯ããŒãå¹ æå
- 空çœã®ããªãšãŒã·ã§ã³: ã¹ããŒã¹ãã¿ããæ¹è¡ãããŒãã¬ãŒã¯ã¹ããŒã¹
-
ãã©ãŒãããåºæã®æå: ã¡ãŒã«ã®
@
ãURLãããã³ã«ãé»è©±çªå·ã®åºåãæå
ç ç©¶ã«ããã°ãå¢çå€åæã¯æååã®ãããªéæ°å€å€æ°ã«æ¡åŒµã§ããããšã瀺ãããŠãããç¹æ®æåãã¹ãã¯å æ¬çãªãã¹ãã«ãã¬ããžã®éèŠãªæ§æèŠçŽ ãšãªããŸãã
4. ç¶æ ãšåæå®è¡ã±ãŒã¹
ãšããžã±ãŒã¹ã¯ããŒã¿ã ãã§ã¯ãããŸããâ ã¿ã€ãã³ã°ãšç¶æ ã«ã€ããŠã§ãã2人ã®ãŠãŒã¶ãŒãåæã«åããã¿ã³ãã¯ãªãã¯ãããã©ããªãã?ãããã¯ãŒã¯ãªã¯ãšã¹ããæäœã®éäžã§ã¿ã€ã ã¢ãŠãããã?ãããã®åæå®è¡ãšç¶æ é·ç§»ã®ãšããžã±ãŒã¹ã¯ãåçŸãéåžžã«å°é£ã§ãããæ¬çªç°å¢ã§ã¯å£æ» çãªåœ±é¿ãäžããŸãã
ç¶æ /åæå®è¡ã«ããŽãªãŒ:
- ã¬ãŒã¹ã³ã³ãã£ã·ã§ã³: å ±æãªãœãŒã¹ãžã®åæã¢ã¯ã»ã¹
- ç¡å¹ãªç¶æ é·ç§»: ééã£ãã©ã€ããµã€ã¯ã«ç¶æ ã§ã®æäœã®è©Šè¡
- ã¿ã€ã ã¢ãŠãã·ããªãª: ãããã¯ãŒã¯ã¿ã€ã ã¢ãŠããããŒã¿ããŒã¹ã¿ã€ã ã¢ãŠããé·æéå®è¡æäœ
- ãªãã©ã€ããžãã¯: åªçæ§ãéè€ãªã¯ãšã¹ãåŠç
- ãªãœãŒã¹æ¯æž: æ¥ç¶ããŒã«ã®æ¯æžãã¡ã¢ãªå¶éãã¹ã¬ãã飢é€
5. æé»çèŠä»¶:è¿°ã¹ãããŠããªãå¥çŽ
ããã§ãšããžã±ãŒã¹ãã³ãã£ã³ã°ã¯æ¢åµäœæ¥ã«ãªããŸãã**æé»çèŠä»¶ã¯ãã¹ããŒã¯ãã«ããŒãè¡ããæ±ºããŠææžåããªãä»®å®ã§ãã**ãããã¯ãæ¬çªç°å¢ã§Xã倱æãããšãã«ã®ã¿è¡šé¢åãããæããã«Xããã¹ãããšããã¹ããŒãã¡ã³ãã§ãã
æé»çèŠä»¶ã«é¢ããç ç©¶ã«ããã°ããããã¯çµéšãšã¢ããªã±ãŒã·ã§ã³ã®é©åãªçè§£ã«åºã¥ããŠè¿œå ãŸãã¯åæãããèŠä»¶ã§ãâã¯ã©ã€ã¢ã³ããå¿ ãããæç¢ºã«è¿°ã¹ãããšãã§ããªãæœåšçãªåé¡ãç¹å®ããããšã¯ããœãããŠã§ã¢ãšã³ãžãã¢ã®è²¬ä»»ã§ãã
æé»çèŠä»¶ã®äŸ:
- ããã©ãŒãã³ã¹: ãããŒãžã¯éãèªã¿èŸŒãŸããã¹ãã(ãããã©ããããéã?100ms?3ç§?)
- 容é: ãè€æ°ã®ãŠãŒã¶ãŒãåŠçããã(10ãŠãŒã¶ãŒ?10,000?)
- ããŒã¿æ€èšŒ: ãã¡ãŒã«ã¢ãã¬ã¹ãåãå ¥ããã(ãããã©ã®RFCæšæº?ãã©ã¹ã¢ãã¬ãã·ã³ã°ãèš±å¯?)
- ãšã©ãŒåŠç: ããŠãŒã¶ãŒã«ãšã©ãŒã衚瀺ããã(ãããã»ãã¥ãªãã£ã«ææãªãšã©ãŒã¯?)
- åŸæ¹äºææ§: ãAPIãæŽæ°ããã(ãããæ¢åã®ã¯ã©ã€ã¢ã³ããå£ããªãã?)
æ¢åµãã¯ããã¯: ãã¹ãŠã®æç€ºçèŠä»¶ã«å¯ŸããŠã次ã®ããã«åããããŸã:
- å¢çã«ã©ã®ãããªãšããžã±ãŒã¹ãååšããã?
- æäœã®éäžã§å€±æãããã©ããªãã?
- ã©ã®ãããªã»ãã¥ãªãã£äžã®åœ±é¿ãããã?
- ã©ã®ãããªããã©ãŒãã³ã¹ç¹æ§ãæåŸ ãããã?
- ã©ã®ãããªã¢ã¯ã»ã·ããªãã£ã®èæ ®äºé ãé©çšãããã?
Constructor Injection:ãã¹ãå¯èœæ§ã®ããã®èšèš
ãšããžã±ãŒã¹ãã¹ãã¯ãã³ãŒãã«é ããäŸåé¢ä¿ãããå Žåãææ°é¢æ°çã«å°é£ã«ãªããŸãã Constructor injectionã¯ãšããžã±ãŒã¹ãã³ã¿ãŒã®ç§å¯å µåš ã§ãããªããªããäŸåé¢ä¿ãæç€ºçã«ããé ããçµåãæé€ãããã¹ãäžã®äŸåé¢ä¿ã®çœ®ãæããå¯èœã«ããããã§ãã
ãªãConstructor Injectionãªã®ã?
äŸåæ§æ³šå ¥ãã¿ãŒã³ã«é¢ããç ç©¶ã¯ãconstructor injectionãå¿ é ã®äŸåé¢ä¿ã«å¯ŸããŠå¥œãŸããçç±ã瀺ããŠããŸã:
- æç€ºçãªäŸåé¢ä¿: ãã¹ãŠã®äŸåé¢ä¿ãã³ã³ã¹ãã©ã¯ã¿ã·ã°ããã£ã§å¯èŠ
- äžå€æ§: ãªããžã§ã¯ãã¯ãã¹ãŠã®äŸåé¢ä¿ãšãšãã«äžåºŠæ§ç¯å¯èœ
- ãã¹ãå¯èœæ§: ãšããžã±ãŒã¹ãã¹ãã®ããã«ã¢ãã¯/ã¹ã¿ããç°¡åã«æ³šå ¥
- ãã§ã€ã«ãã¡ã¹ã: äžè¶³ããŠããäŸåé¢ä¿ã¯å³åº§ã«æ§ç¯å€±æãåŒãèµ·ãã
ã¢ã³ããã¿ãŒã³:é ããäŸåé¢ä¿
// ã¢ã³ããã¿ãŒã³: é ããäŸåé¢ä¿ã¯ãšããžã±ãŒã¹ãã¹ããäžå¯èœã«ãã
class OrderProcessor {
processOrder(order: Order): void {
// ã°ããŒãã«ç¶æ
ãžã®é ããäŸåé¢ä¿ - ãšã©ãŒã·ããªãªãã©ããã¹ããã?
const paymentGateway = PaymentGateway.getInstance();
const emailService = new EmailService();
try {
paymentGateway.charge(order.total);
emailService.sendConfirmation(order.email);
} catch (error) {
// ã¿ã€ã ã¢ãŠãã·ããªãªãã©ããã¹ããã? ãããã¯ãŒã¯é害? ç¡å¹ãªå¿ç?
console.error('Order processing failed', error);
}
}
}
ãã¹ããäžå¯èœãªãšããžã±ãŒã¹:
- 決æžã²ãŒããŠã§ã€ã®ã¿ã€ã ã¢ãŠã
- 決æžã²ãŒããŠã§ã€ãç¡å¹ãªå¿çãè¿ã
- ã¡ãŒã«ãµãŒãã¹ã®ã¯ã©ãŒã¿è¶ é
- æäœã®éäžã§ãããã¯ãŒã¯æ¥ç¶åªå€±
- åææ³šæåŠçã®ã¬ãŒã¹ã³ã³ãã£ã·ã§ã³
解決ç:ãšããžã±ãŒã¹ãã¹ãã®ããã®Constructor Injection
// ãã¿ãŒã³: constructor injectionã¯å
æ¬çãªãšããžã±ãŒã¹ãã¹ããå¯èœã«ãã
interface IPaymentGateway {
charge(amount: number): Promise<PaymentResult>;
}
interface IEmailService {
sendConfirmation(email: string, orderDetails: any): Promise<void>;
}
class OrderProcessor {
constructor(
private readonly paymentGateway: IPaymentGateway,
private readonly emailService: IEmailService
) {}
async processOrder(order: Order): Promise<OrderResult> {
// äŸåé¢ä¿ã泚å
¥ããã - ä»ããã¹ãå¯èœ
const paymentResult = await this.paymentGateway.charge(order.total);
if (!paymentResult.success) {
throw new PaymentFailedError(paymentResult.reason);
}
await this.emailService.sendConfirmation(order.email, order);
return { success: true, orderId: order.id };
}
}
// ä»ãå®éã®å®è£
ã§ãšããžã±ãŒã¹ããã¹ãã§ãã(ã¢ãã¯äžèŠ!)
describe('OrderProcessor - Edge Cases', () => {
it('should handle payment gateway timeout', async () => {
// 100msåŸã«ã¿ã€ã ã¢ãŠãããå®éã®ãã¹ãå®è£
class TimeoutPaymentGateway implements IPaymentGateway {
async charge(amount: number): Promise<PaymentResult> {
await new Promise(resolve => setTimeout(resolve, 5000)); // ã¿ã€ã ã¢ãŠããã·ãã¥ã¬ãŒã
return { success: false, reason: 'timeout' };
}
}
const processor = new OrderProcessor(
new TimeoutPaymentGateway(),
new FakeEmailService()
);
await expect(processor.processOrder(testOrder))
.rejects.toThrow(PaymentFailedError);
});
it('should handle email service quota exceeded', async () => {
class QuotaExceededEmailService implements IEmailService {
async sendConfirmation(email: string, details: any): Promise<void> {
throw new Error('Daily quota exceeded');
}
}
const processor = new OrderProcessor(
new SuccessfulPaymentGateway(),
new QuotaExceededEmailService()
);
// 決æžã¯æåãããã¡ãŒã«ã倱æãã - ã©ããªã?
await expect(processor.processOrder(testOrder))
.rejects.toThrow('Daily quota exceeded');
});
it('should handle invalid email address format edge case', async () => {
const invalidOrder = { ...testOrder, email: 'not-an-email' };
const processor = new OrderProcessor(
new SuccessfulPaymentGateway(),
new ValidatingEmailService() // ã¡ãŒã«ãã©ãŒããããæ€èšŒ
);
await expect(processor.processOrder(invalidOrder))
.rejects.toThrow(InvalidEmailError);
});
});
ã¢ãã¯ã䜿çšããªãã£ãããšã«æ³šæããŠãã ããâ ãã¹ãçšã«èšèšãããå®éã®å®è£ ã䜿çšããŸãããããã¯ã¢ãã¯ããªãŒãã¹ãã§ã:constructor injectionã¯ãã¢ãã¯ãã¬ãŒã ã¯ãŒã¯ã®è€éããªãã«å®éã®ãšããžã±ãŒã¹ã®ããã«åäœãã軜éãªãã¹ãå®è£ ãäœæå¯èœã«ããŸãã
ãã¹ãã®æŽç:æ¢åµã®èšŒæ ããŒã
å æ¬çãªãšããžã±ãŒã¹ãã¹ãã¹ã€ãŒãã¯ãããã«å§åçã«ãªãå¯èœæ§ããããŸããæŽçã¯éèŠã§ãâä¿å®æ§ã®ããã ãã§ãªãã ãšããžã±ãŒã¹ãå¿ãããããåªå é äœãäžãããããããªãããã«ãããã ã§ãã
ãã¹ãæŽçã®åå
- ã¡ãœããã§ã¯ãªãã·ããªãªã§ã°ã«ãŒãå: ãã¹ãã¯ã¹ããŒãªãŒãèªãã¹ã
-
説æçãªãã¹ãåã䜿çš:
shouldRejectEmailWithMultipleAtSymbols
ã§ããtestEmail2
ã§ã¯ãªã - ããããŒãã¹ãšãšããžã±ãŒã¹ãåé¢: ãšããžã±ãŒã¹ã®ã«ãã¬ããžãæç€ºçã«ãã
- ãšããžã±ãŒã¹ã¿ã€ãã§ã¿ã°ä»ããŸãã¯ã«ããŽãªãŒå: å¢çãnullãã»ãã¥ãªãã£ãããã©ãŒãã³ã¹
- æé»çèŠä»¶ãææžå: ãšããžã±ãŒã¹ããªãéèŠããã³ã¡ã³ã
æšå¥šããããã¹ãæ§é
describe('UserRegistration', () => {
describe('Happy Path', () => {
it('should register user with valid standard input', () => {
// åäžã®ããããŒãã¹ãã¹ã
});
});
describe('Boundary Value Edge Cases', () => {
it('should reject username shorter than minimum length', () => {});
it('should reject username longer than maximum length', () => {});
it('should accept username at exact minimum length', () => {});
it('should accept username at exact maximum length', () => {});
});
describe('Null and Empty Value Edge Cases', () => {
it('should reject null username', () => {});
it('should reject undefined username', () => {});
it('should reject empty string username', () => {});
it('should reject whitespace-only username', () => {});
});
describe('Special Character and Format Edge Cases', () => {
it('should reject username with SQL injection attempt', () => {});
it('should reject username with XSS payload', () => {});
it('should handle Unicode characters correctly', () => {});
it('should reject username starting with number', () => {});
});
describe('Security Edge Cases', () => {
it('should reject commonly compromised passwords', () => {});
it('should rate-limit registration attempts', () => {});
it('should prevent duplicate email registration', () => {});
});
describe('Implicit Requirement Edge Cases', () => {
it('should trim whitespace from username input', () => {
// æé»ç: ãŠãŒã¶ãŒã¯å¶çºçãªã¹ããŒã¹ã§ç»é²ã«å€±æãã¹ãã§ãªã
});
it('should normalize email address case', () => {
// æé»ç: User@Example.comã¯user@example.comãšçãããªãã¹ã
});
it('should complete registration within 3 seconds', () => {
// æé»çããã©ãŒãã³ã¹èŠä»¶
});
});
});
ãã¹ãã«ãã¬ããžã®çœ :100%ã«ãã¬ããžâ å æ¬çãã¹ã
ããã«äžå¿«ãªçå®ããããŸã:**100%ã®ã³ãŒãã«ãã¬ããžããã£ãŠããéèŠãªãšããžã±ãŒã¹ãèŠéãå¯èœæ§ããããŸãã**ã³ãŒãã«ãã¬ããžã¯ããã¹ãäžã«ã©ã®è¡ãå®è¡ãããããæž¬å®ããŸãâã©ã®åäœãæ€èšŒãããŠãããããŸãã¯ã©ã®ãšããžã±ãŒã¹ãæ¢çŽ¢ãããŠãããã§ã¯ãããŸããã
ãã¹ãã«ãã¬ããžæè¡ã«é¢ããç ç©¶ã瀺ãããã«ãå æ¬çãªã«ãã¬ããžã«ã¯è€æ°ã®æŠç¥ã®çµã¿åãããå¿ èŠã§ã:å¢çå€åæãåå€åå²ãæ¢çŽ¢çãã¹ããAIæ¯æŽã«ãããšããžã±ãŒã¹èå¥ã
ã«ãã¬ããžã¡ããªã¯ã¹ãèŠéããã®
// ãã®é¢æ°ã¯åäžã®ãã¹ãã§100%ã®ã³ãŒãã«ãã¬ããžãéæ
function divide(a: number, b: number): number {
return a / b;
}
// 100%ã«ãã¬ããžãéæããåäžã®ãã¹ã
it('should divide two numbers', () => {
expect(divide(10, 2)).toBe(5);
});
100%ã«ãã¬ããžã«ããããããèŠéããããšããžã±ãŒã¹:
- ãŒãã«ããé€ç®:
divide(10, 0)
âInfinity
- è² ã®æ°ã§ã®é€ç®:
divide(-10, 2)
â-5
- æµ®åå°æ°ç¹ã«ãªãé€ç®:
divide(10, 3)
â3.3333...
- null/undefinedã§ã®é€ç®:
divide(null, 2)
âNaN
- éåžžã«å€§ããªæ°ã§ã®é€ç®:
divide(Number.MAX_VALUE, 0.1)
âInfinity
ã«ãã¬ããžãè¶ ããŠ:ãšããžã±ãŒã¹ã¡ããªã¯ã¹
ã«ãã¬ããžããŒã»ã³ããŒãžã远ãããã代ããã«ã以äžã远跡ããŸã:
- ãã¹ãããããšããžã±ãŒã¹ã«ããŽãªãŒ: å¢çãnullããã©ãŒããããªã©ã®ãã¹ãã¯ããã€ååšããã?
- ææžåãããæé»çèŠä»¶: ä»®å®ã¯ãã¹ãããææžåãããŠããã?
- 鲿¢ãããæ¬çªãã°: ãšããžã±ãŒã¹ãã¹ãã¯ãããã€åã«ãã°ãæããã?
- 鲿¢ãããã»ãã¥ãªãã£è匱æ§: ãã¹ãã¯ã€ã³ãžã§ã¯ã·ã§ã³è©Šè¡ããªãŒããŒãããŒãæããã?
- ãã¹ããšã³ãŒãã®æ¯ç: éèŠãªãã¹ã§ã¯é«ããäºçްãªã³ãŒãã§ã¯äœã
ãšããžã±ãŒã¹ãã³ã¿ãŒã®ããŒã«ããã:å®è·µçãã¯ããã¯
1. åå€åå² + å¢çå€åæ
ãããã®æè¡ãçµã¿åãããŠãäœç³»çã«ãšããžã±ãŒã¹ãçæããŸã:
äŸ: å²åŒèšç®æ©ã®ãã¹ã
- åå€åå²: å²åŒãªã(0-$49)ã10%å²åŒ($50-$99)ã20%å²åŒ($100+)
- å¢çå€: $0ã$49ã$50ã$99ã$100ã$1,000,000
- ãšããžã±ãŒã¹: è² ã®éé¡ãnullãéæ°å€å ¥åãé貚粟床
2. ããããã£ããŒã¹ããã¹ã
åã
ã®ãã¹ãã±ãŒã¹ãæžã代ããã«ãåžžã«ä¿æãããã¹ãããããã£ãå®çŸ©ããŸã:
// fast-checkã©ã€ãã©ãªã®äŸ
import fc from 'fast-check';
it('should always produce idempotent results', () => {
fc.assert(
fc.property(fc.string(), (input) => {
const result1 = normalizeEmail(input);
const result2 = normalizeEmail(result1);
return result1 === result2; // æ£èŠåã¯åªç
})
);
});
3. ãã¥ãŒããŒã·ã§ã³ãã¹ã
StrykerãPITã®ãããªããŒã«ã¯ãã³ãŒãã«ãã¥ãŒã¿ã³ã(æå³çãªãã°)ãäœæããŸãããã¥ãŒããŒã·ã§ã³ããã£ãŠããã¹ããééããå Žåããšããžã±ãŒã¹ã«ãã¬ããžã¯äžååã§ãã
4. ãã¬ã€ã³ã¹ããŒãã³ã°ã»ãã·ã§ã³
ããŒã ã®çµéšã掻çšããŠãååçãªãã¬ã€ã³ã¹ããŒãã³ã°ãéããŠãšããžã±ãŒã¹ãç¹å®ããŸããåããããŸã:
- ããŠãŒã¶ãŒãæäŸã§ããææªã®å ¥åã¯äœã?ã
- ããã®å€éšãµãŒãã¹ãããŠã³ãããã©ããªãã?ã
- ãæªæã®ããã¢ã¯ã¿ãŒã¯ãããã©ãæªçšããã?ã
å®äžçã®ãšããžã±ãŒã¹æŠèš
ã±ãŒã¹ã¹ã¿ãã£1:é幎ãã°
決æžåŠçã·ã¹ãã ã365æ¥ã远å ããŠãæ¥å¹Žããèšç®ããŠããŸãããå®ç§ã«æ©èœããŠããŸããâ2020幎2æ29æ¥ãŸã§ã2021幎ã«ã¹ã±ãžã¥ãŒã«ãããæ¯æãã1æ¥ãããŠããŸããã èŠéããããšããžã±ãŒã¹: é幎ã®å¢çã
æèš: é幎ããµããŒã¿ã€ã é·ç§»ãã¿ã€ã ãŸãŒã³ã®ç«¯ãè¶ããŠæ¥ä»å¢çããã¹ãããã
ã±ãŒã¹ã¹ã¿ãã£2:Unicodeã¡ãŒã«ã€ã³ã·ãã³ã
ã¡ãŒã«ããªããŒã·ã§ã³é¢æ°ãã·ã³ãã«ãªæ£èŠè¡šçŸã䜿çšããŠããŸãã:^[a-zA-Z0-9@.-]+$
ãããŸãæ©èœããŠããŸããâãã€ã人ãŠãŒã¶ãŒãmÃŒller@example.com
ã§ç»é²ããããšãããŸã§ã èŠéããããšããžã±ãŒã¹: åœéæåã
æèš: Unicodeãçµµæåãåœéãã¡ã€ã³åããã¹ããããçŸä»£ã®ã¡ãŒã«æšæº(RFC 5322)ã¯ASCIIããã¯ããã«å€ãããµããŒãããŠããŸãã
ã±ãŒã¹ã¹ã¿ãã£3:æ¬çªç°å¢ã®Null Pointer
ã·ã§ããã³ã°ã«ãŒã颿°ãé ç®é åãåžžã«ååšãããšä»®å®ããŠããŸããããã¹ãã§ã¯å®ç§ã«æ©èœããŸããâãã¹ãŠã®ãã¹ããé ç®ä»ãã«ãŒããäœæããŠããŸããããã®åŸãæ¬çªãšããžã±ãŒã¹:空ã®ã«ãŒããæã€ãŠãŒã¶ãŒãnullãã€ã³ã¿äŸå€ãåŒãèµ·ãããŸããã èŠéããããšããžã±ãŒã¹: 空ã®ã³ã¬ã¯ã·ã§ã³ã
æèš: ãã¹ãŠã®ã³ã¬ã¯ã·ã§ã³ãšãªãã·ã§ã³å€ã«å¯ŸããŠnullãundefinedã空ã®ç¶æ ããã¹ãããã
ãšããžã±ãŒã¹ãã³ã¿ãŒã®ãã§ãã¯ãªã¹ã
æ©èœãã宿ããšããŒã¯ããåã«ããã®ãã§ãã¯ãªã¹ããå®è¡ããŠãã ãã:
å ¥åæ€èšŒãšããžã±ãŒã¹
- Nullãundefinedã空ã®å€ããã¹ããããŠãã
- å¢çå€ããã¹ããããŠãã(minãmaxããŒããè² )
- ç¹æ®æåããã¹ããããŠãã(SQLãXSSããã¹ãã©ããŒãµã«)
- Unicodeãšçµµæåããã¹ããããŠãã
- æå€§é·/ãµã€ãºããã¹ããããŠãã
- ç¡å¹ãªãã©ãŒãããããã¹ããããŠãã
ããžãã¹ããžãã¯ãšããžã±ãŒã¹
- ç¶æ é·ç§»ãšããžã±ãŒã¹ããã¹ããããŠãã
- åæã¢ã¯ã»ã¹ã·ããªãªããã¹ããããŠãã
- ã¿ã€ã ã¢ãŠããšãªãã©ã€ããžãã¯ããã¹ããããŠãã
- ç¡å¹ãªç¶æ ã®çµã¿åããããã¹ããããŠãã
- ããŒã«ããã¯/è£åããžãã¯ããã¹ããããŠãã
ã»ãã¥ãªãã£ãšããžã±ãŒã¹
- ã€ã³ãžã§ã¯ã·ã§ã³è©Šè¡ããã¹ããããŠãã(SQLãXSSãã³ãã³ã)
- èªèšŒ/èªå¯ã®å¢çã±ãŒã¹ããã¹ããããŠãã
- ã¬ãŒãå¶éããã¹ããããŠãã
- å ¥åãµãã¿ã€ãŒãŒã·ã§ã³ãæ€èšŒãããŠãã
- æ©å¯ããŒã¿ã®é²åºã鲿¢ãããŠãã
ããã©ãŒãã³ã¹ãšããžã±ãŒã¹
- 倧éããŒã¿ããªã¥ãŒã ããã¹ããããŠãã
- ã¡ã¢ãªå¶éããã¹ããããŠãã
- ã¿ã€ã ã¢ãŠãã·ããªãªããã¹ããããŠãã
- åæè² è·ããã¹ããããŠãã
- ãªãœãŒã¹æ¯æžã·ããªãªããã¹ããããŠãã
æé»çèŠä»¶ã®æ€èšŒ
- ããã©ãŒãã³ã¹æåŸ ãææžåããããã¹ããããŠãã
- 容éå¶éãç¹å®ããããã¹ããããŠãã
- ã¢ã¯ã»ã·ããªãã£èŠä»¶ããã¹ããããŠãã
- ãšã©ãŒã¡ãã»ãŒãžã®æç¢ºæ§ãæ€èšŒãããŠãã
- åŸæ¹äºææ§ãæ€èšŒãããŠãã
çµè«:é²åŸ¡çãã¹ãã®æèž
ãšããžã±ãŒã¹ãã¹ãã¯åŠæ³ã«ã€ããŠã§ã¯ãããŸããâ è·äººæ ã«ã€ããŠã§ããããã¯ãåããã³ãŒããšèããã³ãŒãã®éãã§ããããªããæžããã¹ãŠã®ãšããžã±ãŒã¹ãã¹ãã¯ãé²ãæ¬çªãã°ãéããã»ãã¥ãªãã£è匱æ§ãé¿ãããŠãŒã¶ãŒã®äžæºã§ãã
ãšããžã±ãŒã¹ãã³ã¿ãŒã®ãã€ã³ãã»ããã¯ããã¹ãããã§ãã¯ãªã¹ããã調æ»ãžãšå€æããŸã:
- TDDã䜿çšããŠå®è£ åã«åäœãå®çŸ©ãã æåã«ãã¹ããæžã
- ãã¹ãŠã®ã¹ãããã§ãäœãééãå¯èœæ§ãããã?ããšåããã㊠é²åŸ¡çã«èãã
- ãšããžã±ãŒã¹å顿³(å¢çãnullããã©ãŒããããç¶æ ãæé»)ã䜿çšã㊠äœç³»çã«ã«ããŽãªãŒå
- constructor injectionãšæç€ºçãªäŸåé¢ä¿ã§ ãã¹ãå¯èœæ§ã®ããã«èšèš
- ãšããžã±ãŒã¹ãå¯èŠã§ä¿å®å¯èœã§ããç¶ããããã« ç¶¿å¯ã«æŽç
- ã³ãŒãã«ãã¬ããžãè¶ ããŠãšããžã±ãŒã¹ã«ãã¬ããžãž éèŠãªãã®ã枬å®
Kent BeckãæãåºãããŠãããããã«ãTDDã¯ãèšèšã®éèŠãªãã€ã³ãã«è¿ éã«å°ãããã«ãã¹ããé©åã«é åºä»ããããšãã«ã€ããŠã§ãããšããžã±ãŒã¹ã¯ãããã®éèŠãªãã€ã³ãã§ãâãããã¯ããªãã®èšèšãçŸå®ã®æ··æ²ãšåºäŒãå Žæã§ãã
次åãã¹ããæžããšããããããŒãã¹ã®åã«äžæåæ¢ããŠãã ãããèªåããŠãã ãã:ããããå£ããã®ã¯äœã?äœãä»®å®ããŠããã?äœãèæ ®ããŠããªãã?ããã®åŸããããã®ãã¹ããæžããŠãã ãããå°æ¥ã®ããªãèªèº«âãããŠããªãã®ãŠãŒã¶ãŒâãæè¬ããã§ãããã
åèæç®
: [1] Beck, Kent. Test Driven Development: By Example. Addison-Wesley Professional, 2002. O'Reilly
: [2] Fowler, Martin. "Test Driven Development." Martin Fowler's Bliki, 2005. martinfowler.com
: [3] Holota, Olha. "Explore the Power of Boundary Value Analysis in Software Testing." Medium, 2024. Medium
: [4] Hoare, Tony. "Null References: The Billion Dollar Mistake." InfoQ, 2009.
: [5] Singh, Gurpreet. "Boundary Value Analysis for Non-Numerical Variables: Strings." Oriental Journal of Computer Science and Technology, 2010. OJCST
: [6]"Implicit Requirements." GeekInterview, 2024. GeekInterview
: [7] Khan, Sardar. "Understanding Dependency Injection: A Powerful Design Pattern for Flexible and Testable Code." Medium, 2024. Medium
: [8]"Boost Your Test Coverage: Techniques & Best Practices." Muuktest Blog, 2024. Muuktest
: [9]"Understanding Equivalence Partitioning and Boundary Value Analysis in Software Testing." SDET Unicorns, 2024. SDET Unicorns
: [10]"Identifying Test Edge Cases: A Practical Approach." Frugal Testing Blog, 2024. Frugal Testing
: [11] Resnick, P. "RFC 5322 - Internet Message Format." IETF, 2008.
Originally published at kanaeru.ai
Top comments (0)