- TDD ๋ฒ์น ์ธ ๊ฐ์ง
- ๊นจ๋ํ ํ ์คํธ ์ฝ๋ ์ ์งํ๊ธฐ
- ๊นจ๋ํ ํ ์คํธ ์ฝ๋
- ํ ์คํธ ๋น assert ํ๋
- F.I.R.S.T
- ๊ฒฐ๋ก
- ์ฒซ์งธ ๋ฒ์น: ์คํจํ๋ ๋จ์ ํ ์คํธ๋ฅผ ์์ฑํ ๋๊น์ง ์ค์ ์ฝ๋๋ฅผ ์์ฑํ์ง ์๋๋ค.
- ๋์งธ ๋ฒ์น: ์ปดํ์ผ์ ์คํจํ์ง ์์ผ๋ฉด์ ์คํ์ด ์คํจํ๋ ์ ๋๋ก๋ง ๋จ์ ํ ์คํธ๋ฅผ ์์ฑํ๋ค.
- ์ ์งธ ๋ฒ์น: ํ์ฌ ์คํจํ๋ ํ ์คํธ๋ฅผ ํต๊ณผํ ์ ๋๋ก๋ง ์ค์ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
์ ์ธ๊ฐ์ง ๊ท์น์ ๋ฐ๋ฅด๋ฉด์ ์ผํ๋ฉด ์ค์ ์ฝ๋๋ฅผ ์ฌ์ค์ ์ ๋ถ ํ ์คํธํ๋ ํ ์คํธ ์ผ์ด์ค๊ฐ ๋์จ๋ค. ํ์ง๋ง ์ค์ ์ฝ๋์ ๋ง๋จน์ ์ ๋๋ก ๋ฐฉ๋ํ ํ ์คํธ ์ฝ๋๋ ์ฌ๊ฐํ ๊ด๋ฆฌ ๋ฌธ์ ๋ฅผ ์ ๋ฐํ๊ธฐ๋ ํ๋ค.
์ค์ ์ฝ๋๊ฐ ์งํํ๋ฉด ํ ์คํธ ์ฝ๋๋ ๋ณํด์ผ ํ๋ค. ๊ทธ๋ฐ๋ฐ ํ ์คํธ ์ฝ๋๊ฐ ์ง์ ๋ถํ ์๋ก ๋ณ๊ฒฝํ๊ธฐ ์ด๋ ค์์ง๋ค. ์ค์ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํด ๊ธฐ์กด ํ ์คํธ ์ผ์ด์ค๊ฐ ์คํจํ๊ธฐ ์์ํ๋ฉด, ์ง์ ๋ถํ ์ฝ๋๋ก ์ธํด, ์คํจํ๋ ํ ์คํธ ์ผ์ด์ค๋ฅผ ์ ์ ๋ ํต๊ณผ์ํค๊ธฐ ์ด๋ ค์์ง๋ค. ๊ทธ๋์ ํ ์คํธ ์ฝ๋๋ ๊ณ์ํด์ ๋์ด๋๋ ๋ถ๋ด์ด ๋๋ฒ๋ฆฐ๋ค.
ํ ์คํธ ์ฝ๋๋ ์ค์ ์ฝ๋ ๋ชป์ง ์๊ฒ ์ค์ํ๋ค. ํ ์คํธ ์ฝ๋๋ ์ฌ๊ณ ์ ์ค๊ณ์ ์ฃผ์๊ฐ ํ์ํ๋ค. ์ค์ ์ฝ๋ ๋ชป์ง ์๊ฒ ๊นจ๋ํ๊ฒ ์ง์ผ ํ๋ค.
์ฝ๋์ ์ ์ฐ์ฑ, ์ ์ง๋ณด์์ฑ, ์ฌ์ฌ์ฉ์ฑ์ ์ ๊ณตํ๋ ๋ฒํ๋ชฉ์ ๋ฐ๋ก ๋จ์ ํ ์คํธ๋ค. ํ ์คํธ ์ผ์ด์ค๊ฐ ์๋ค๋ฉด ๋ชจ๋ ๋ณ๊ฒฝ์ด ์ ์ ์ ์ธ ๋ฒ๊ทธ๋ค.
ํ ์คํธ ์ผ์ด์ค๊ฐ ์๋ค๋ฉด ์ํคํ ์ฒ๊ฐ ๋ถ์คํ ์ฝ๋๋ ์ค๊ณ๊ฐ ๋ชจํธํ๊ณ ์๋ง์ธ ์ฝ๋๋ผ๋ ๋ณ๋ค๋ฅธ ์ฐ๋ ค ์์ด ๋ณ๊ฒฝํ ์ ์๋ค. ์๋, ์คํ๋ ค ์์ฌํ๊ณ ์ํคํ ์ฒ์ ์ค๊ณ๋ฅผ ๊ฐ์ ํ ์ ์๋ค.
๊ทธ๋ฌ๋ฏ๋ก ์ค์ ์ฝ๋๋ฅผ ์ ๊ฒํ๋ ์๋ํ๋ ๋จ์ ํ ์คํธ ์ํธ๋ ์ค๊ณ์ ์ํคํ ์ฒ๋ฅผ ์ต๋ํ ๊นจ๋ํ๊ฒ ๋ณด์กดํ๋ ์ด์ ๋ค. ํ ์คํธ๋ ์ ์ฐ์ฑ, ์ ์ง๋ณด์์ฑ, ์ฌ์ฌ์ฉ์ฑ์ ์ ๊ณตํ๋ค. ํ ์คํธ ์ผ์ด์ค๊ฐ ์์ผ๋ฉด ๋ณ๊ฒฝ์ด ์ฌ์์ง๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ๋ผ์ ํ ์คํธ ์ฝ๋๊ฐ ์ง์ ๋ถํ๋ฉด ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฅ๋ ฅ์ด ๋จ์ด์ง๋ฉฐ ์ฝ๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ ํ๋ ๋ฅ๋ ฅ๋ ๋จ์ด์ง๋ค.
๊นจ๋ํ ํ ์คํธ ์ฝ๋๋ฅผ ๋ง๋ค๋ ค๋ฉด? ๊ฐ๋ ์ฑ์ด ํ์ํ๋ค. ํ ์คํธ ์ฝ๋์์ ๊ฐ๋ ์ฑ์ ๋์ด๋ ค๋ฉด? ๋ช ๋ฃ์ฑ, ๋จ์์ฑ, ํ๋ถํ ํํ๋ ฅ์ด ํ์ํ๋ค. ํ ์คํธ ์ฝ๋๋ ์ต์ํ์ ํํ์ผ๋ก ๋ง์ ๊ฒ์ ๋ํ๋ด์ผ ํ๋ค.
๋ค์ ์ฝ๋๋ฅผ ๋ณด์. FitNess์์ ๊ฐ์ ธ์จ ์ฝ๋๋ค. ์๋ ํ ์คํธ ์ผ์ด์ค ์ธ ๊ฐ๋ ์ดํดํ๊ธฐ ์ด๋ ต๊ธฐ์ ๊ฐ์ ํ ์ฌ์ง๊ฐ ์ถฉ๋ถํ๋ค.
addPage์assertSubString์ ๋ถ๋ฅด๋๋ผ ์ค๋ณต๋๋ ์ฝ๋๊ฐ ๋งค์ฐ ๋ง๋ค. ์์ง๊ตฌ๋ ํ ์ฌํญ์ด ๋๋ฌด ๋ง์ ํ ์คํธ ์ฝ๋์ ํํ๋ ฅ์ด ๋จ์ด์ง๋ค.PathParserํธ์ถ์ ์ดํด๋ณด๋ฉด,PathParser๋ ๋ฌธ์์ด์pagePath์ธ์คํด์ค๋ก ๋ณํํ๋ค.pagePath๋ ์น ๋ก๋ดcrawler์ด ์ฌ์ฉํ๋ ๊ฐ์ฒด๋ค. ์ด ์ฝ๋๋ ํ ์คํธ์ ๋ฌด๊ดํ๋ฉฐ ํ ์คํธ ์ฝ๋์ ์๋๋ง ํ๋ฆฐ๋ค.responder๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์ฝ๋์response๋ฅผ ์์งํด ๋ณํํ๋ ์ฝ๋ ์ญ์ ์ก์์ ๋ถ๊ณผํ๋ค.resource์ ์ธ์์์ ์์ฒญ URL์ ๋ง๋๋ ์ด์คํ ์ฝ๋๋ ๋ณด์ธ๋ค.- ์ฝ๋ ์ฌ๋์ ๊ณ ๋ คํ์ง ์๋๋ค.
public func testGetPageHieratchyAsXml() throws -> Void {
crawler.addPage(root, PathParser.parse("PageOne"))
crawler.addPage(root, PathParser.parse("PageOne.ChildOne"))
crawler.addPage(root, PathParser.parse("PageTwo"))
request.setResource("root")
request.addInput("type", "pages")
var responder: Responder = SerializedPageResponder()
var response: SimpleResponse = responder.makeResponse(FitNesseContext(root), request) as? SimpleResponse
var xml: String = response.getContent()
assertEquals("text/xml", response.getContentType())
assertSubString("<name>PageOne</name>", xml)
assertSubString("<name>PageTwo</name>", xml)
assertSubString("<name>ChildOne</name>", xml)
}
public func testGetPageHieratchyAsXmlDoesntContainSymbolicLinks() throws -> Void {
var pageOne: WikiPage = crawler.addPage(root, PathParser.parse("PageOne"))
crawler.addPage(root, PathParser.parse("PageOne.ChildOne"))
crawler.addPage(root, PathParser.parse("PageTwo"))
var data: PageData = pageOne.getData()
var properties: WikiPageProperties = data.getProperties()
var symLinks: WikiPageProperty = properties.set(SymbolicPage.PROPERTY_NAME)
symLinks.set("SymPage", "PageTwo")
pageOne.commit(data)
request.setResource("root")
request.addInput("type", "pages")
var responder: Responder = SerializedPageResponder()
var response: SimpleResponse = responder.makeResponse(FitNesseContext(root), request) as? SimpleResponse
var xml: String = response.getContent()
assertEquals("text/xml", response.getContentType())
assertSubString("<name>PageOne</name>", xml)
assertSubString("<name>PageTwo</name>", xml)
assertSubString("<name>ChildOne</name>", xml)
assertNotSubString("SymPage", xml)
}
public func testGetDataAsHtml() throws -> Void {
crawler.addPage(root, PathParser.parse("PageOne"), "test page")
request.setResouce("TestPageOne")
request.addInput("type", "data")
var responder: Responder = SerializedPageResponder()
var response: SimpleResponse = responder.makeResponse(FitNesseContext(root), request) as? SimpleResponse
var xml: String = response.getContent()
assertEquals("text/xml", response.getContentType())
assertSubString("test page", xml)
assertSubString("<Test", xml)
}๋ค์ ์ฝ๋๋ฅผ ์ดํด๋ณด์. ์ ์ฝ๋๋ฅผ ๊ฐ์ ํ ์ฝ๋๋ก, ์ ํํ ๋์ผํ ํ ์คํธ๋ฅผ ์ํํ๋ค. ํ์ง๋ง ์ข ๋ ๊นจ๋ํ๊ณ ์ข ๋ ์ดํดํ๊ธฐ ์ฝ๋ค.
public func testGetPageHieratchyAsXml() throws -> Void {
makePages("PageOne", "PageOne.ChildOne", "PageTwo")
submitRequest("root", "type:pages")
assertResponseIsXml()
assertResponseContains("<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>")
}
public func testGetPageHieratchyAsXmlDoesntContainSymbolicLinks() throws -> Void {
var page: WikiPage = makePage("PageOne")
makePages("PageOne.ChildOne", "PageTwo")
addLinkTo(page, "PageTwo", "SymPage")
submitRequest("root", "type:pages")
assertResponseIsXml()
assertResponseContains("<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>")
assertResponseDoesNotContain("SymPage")
}
public func testGetDataAsHtml() throws -> Void {
makePageWithContent("TestPageOne", "test page")
submitRequest("TestPageOne", "type:data")
assertResponseIsXml()
assertResponseContains("test page", "<Test")
}๊ฐ ํ ์คํธ๋ ๋ช ํํ ์ธ ๋ถ๋ถ์ผ๋ก ๋๋ ์ง๋ค.
- ํ ์คํธ ์๋ฃ๋ฅผ ๋ง๋ ๋ค.
- ํ ์คํธ ์๋ฃ๋ฅผ ์กฐ์ํ๋ค.
- ์กฐ์ํ ๊ฒฐ๊ณผ๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ์ธํ๋ค.
์ก๋คํ๊ณ ์ธ์ธํ ์ฝ๋๋ฅผ ๊ฑฐ์ ๋ค ์์ด๋ค๋ ์ฌ์ค์ ์ฃผ๋ชฉํ๋ค. ํ ์คํธ ์ฝ๋๋ ๋ณธ๋ก ์ ๋์ ํด ์ง์ง ํ์ํ ์๋ฃ ์ ํ๊ณผ ํจ์๋ง ์ฌ์ฉํ๋ค. ๊ทธ๋ฌ๋ฏ๋ก ์ฝ๋๋ฅผ ์ฝ๋ ์ฌ๋์ ์จ๊ฐ ์ก๋คํ๊ณ ์ธ์ธํ ์ฝ๋์ ํท๊ฐ๋ฆด ํ์ ์์ด ์ฝ๋๊ฐ ์ํํ๋ ๊ธฐ๋ฅ์ ์ฌ๋นจ๋ฆฌ ์ดํดํ๋ค.
๋ฐฉ๊ธ ์ ์ ์ฝ๋๋ ๋๋ฉ์ธ์ ํนํ๋ ์ธ์ดDSL๋ก ํ ์คํธ ์ฝ๋๋ฅผ ๊ตฌํํ๋ ๊ธฐ๋ฒ์ ๋ณด์ฌ์ค๋ค. ํํ ์ฐ๋ ์์คํ ์กฐ์ API๋ฅผ ์ฌ์ฉํ๋ ๋์ API ์์๋ค ํจ์์ ์ ํธ๋ฆฌํฐ๋ฅผ ๊ตฌํํ ํ ๊ทธ ํจ์์ ์ ํธ๋ฆฌํฐ๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ํ ์คํธ ์ฝ๋๋ฅผ ์ง๊ธฐ๋ ์ฝ๊ธฐ๋ ์ฌ์์ง๋ค. ์ด๋ ๊ฒ ๊ตฌํํ ํจ์์ ์ ํธ๋ฆฌํฐ๋ ํ ์คํธ ์ฝ๋์์ ์ฌ์ฉํ๋ ํน์ API๊ฐ ๋๋ค. ์ฆ, ํ ์คํธ๋ฅผ ๊ตฌํํ๋ ๋น์ฌ์์ ๋์ค์ ํ ์คํธ๋ฅผ ์ฝ์ด๋ณผ ๋ ์๋ฅผ ๋์์ฃผ๋ ํ ์คํธ ์ธ์ด๋ค.
์๋ จ๋ ๊ฐ๋ฐ์๋ผ๋ฉด ์๊ธฐ ์ฝ๋๋ฅผ ์ข ๋ ๊ฐ๊ฒฐํ๊ณ ํํ๋ ฅ์ด ํ๋ถํ ์ฝ๋๋ก ๋ฆฌํฉํฐ๋งํด์ผ ๋ง๋ ํ๋ค.
ํ ์คํธ API ์ฝ๋์ ์ ์ฉํ๋ ํ์ค์ ์ค์ ์ฝ๋์ ์ ์ฉํ๋ ํ์ค๊ณผ ํ์คํ ๋ค๋ฅด๋ค. ๋จ์ํ๊ณ , ๊ฐ๊ฒฐํ๊ณ , ํํ๋ ฅ์ด ํ๋ถํด์ผ ํ์ง๋ง, ์ค์ ์ฝ๋๋งํผ ํจ์จ์ ์ผ ํ์๋ ์๋ค.
@Test
public func turnOnLoTempAlarmAtThreashold() throws -> Void {
hw.setTemp(WAY_TOO_COLD);
controller.tic();
assertTrue(hw.heaterState());
assertTrue(hw.blowerState());
assertFalse(hw.coolerState());
assertFalse(hw.hiTempAlarm());
assertTrue(hw.loTempAlarm());
}์ ์ฝ๋๋ฅผ ์ฝ์ผ๋ฉด ์ฝ๋์์ ์ ๊ฒํ๋ ์ํ ์ด๋ฆ๊ณผ ์ํ ๊ฐ์ ํ์ธํ๋๋ผ ๋๊ธธ์ด ์ด๋ฆฌ์ ๋ฆฌ ํฉ์ด์ง๋ค. heaterState๋ผ๋ ์ํ๋ฅผ ๋ณด๊ณ ์๋ ์ผ์ชฝ์ผ๋ก ๋๊ธธ์ ๋๋ ค assertTrue๋ฅผ ์ฝ๋๋ค. ํ
์คํธ ์ฝ๋๋ฅผ ์ฝ๊ธฐ๊ฐ ์ด๋ ต๋ค.
@Test
public func turnOnLoTempAlarmAtThreashold() throws -> Void {
wayTooCold()
assertEquals("HBchL", hw.getState())
}tic ํจ์๋ wayTooCold๋ผ๋ ํจ์๋ฅผ ๋ง๋ค์ด ์จ๊ฒผ๋ค. assertEquals์ ๋ค์ด์๋ ์ด์ํ ๋ฌธ์์ด์ ์ฃผ๋ชฉํ์. ๋๋ฌธ์๋ '์ผ์งon'์ด๊ณ ์๋ฌธ์๋ '๊บผ์งoff'์ ๋ปํ๋ค. ๋ฌธ์๋ ํญ์ {heater, blower, cooler, hi-temp-alarm, lo-temp-alarm} ์์๋ค.
๋น๋ก ์ ๋ฐฉ์์ด ๊ทธ๋ฆ๋ ์ ๋ณด๋ฅผ ํผํ๋ผ๋ ๊ท์น์ ์๋ฐ์ ๊น๊ฐ์ง๋ง ์ฌ๊ธฐ์๋ ์ ์ ํด๋ณด์ธ๋ค. ์ผ๋จ ์๋ฏธ๋ง ์๋ค๋ฉด ๋๊ธธ์ด ๋ฌธ์์ด์ ๋ฐ๋ผ ์์ง์ด๋ฉฐ ๊ฒฐ๊ณผ๋ฅผ ์ฌ๋นจ๋ฆฌ ํ๋จํ๋ค.
public func getState() -> String {
var state = ""
state += heater ? "H" : "h"
state += blower ? "B" : "b"
state += cooler ? "C" : "c"
state += hiTempAlarm ? "H" : "h"
state += lotempAlarm ? "L" : "l"
return state
}์ฝ๋๊ฐ ๊ทธ๋ฆฌ ํจ์จ์ ์ด์ง ๋ชปํ๋ค. (์๋ฐ์์) ํจ์จ์ ๋์ด๋ ค๋ฉด StringBuffer๊ฐ ๋ ์ ํฉํ๋ค. ์ด ์ ํ๋ฆฌ์ผ์ด์
์ ์ค์๊ฐ ์๋ฒ ๋๋ ์์คํ
์ด๋ค. ์ฆ, ์ปดํจํฐ ์์๊ณผ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ ํ์ ์ผ ๊ฐ๋ฅ์ฑ์ด ๋๋ค. ํ์ง๋ง ํ
์คํธ ํ๊ฒฝ์ ์์์ด ์ ํ์ ์ผ ๊ฐ๋ฅ์ฑ์ด ๋ฎ๋ค.
์ด๊ฒ์ด ์ด์ค ํ์ค์ ๋ณธ์ง์ด๋ค. ์ค์ ํ๊ฒฝ์์๋ ์ ๋๋ก ์ ๋์ง๋ง ํ ์คํธ ํ๊ฒฝ์์๋ ์ ํ ๋ฌธ์ ์๋ ๋ฐฉ์์ด ์๋ค. ๋๊ฒ ๋ฉ๋ชจ๋ฆฌ๋ CPU ํจ์จ๊ณผ ๊ด๋ จ ์๋ ๊ฒฝ์ฐ๋ค. ์ฝ๋์ ๊นจ๋ํจ๊ณผ๋ ์ฒ ์ ํ ๋ฌด๊ดํ๋ค.
JUnit์ผ๋ก ํ
์คํธ ์ฝ๋๋ฅผ ์งค ๋๋ ํจ์๋ง๋ค assert ๋ฌธ์ ๋จ ํ๋๋ง ์ฌ์ฉํด์ผ ํ๋ค๊ณ ์ฃผ์ฅํ๋ ํํ๊ฐ ์๋ค. ๊ฐํนํ ๊ท์น์ด๋ผ ์ฌ๊ธธ์ง๋ ๋ชจ๋ฅด์ง๋ง, assert ๋ฌธ์ด ๋จ ํ๋์ธ ํจ์๋ ๊ฒฐ๋ก ์ด ํ๋๋ผ์ ์ฝ๋๋ฅผ ์ดํดํ๊ธฐ ์ฝ๊ณ ๋น ๋ฅด๋ค.
๊นจ๋ํ ํ
์คํธ ์ฝ๋์์ ๋ดค๋ ์ฝ๋๋ ์ด๋จ๊น? "์ถ๋ ฅ์ด XML์ด๋ค"๋ผ๋ assert ๋ฌธ๊ณผ "ํน์ ๋ฌธ์์ด์ ํฌํจํ๋ค"๋ assert ๋ฌธ์ ํ๋๋ก ๋ณํฉํ๋ ๋ฐฉ์์ด ๋ถํฉ๋ฆฌํด ๋ณด์ธ๋ค. ํ์ง๋ง ๋ค์ ์ฝ๋์ ๊ฐ์ด ํ
์คํธ๋ฅผ ๋ ๊ฐ๋ก ์ชผ๊ฐ ๊ฐ์๊ฐ assert๋ฅผ ์ํํ๋ฉด ๋๋ค.
public func testGetPageHierarchyAsXml() throws -> Void {
givenPages("PageOne", "PageOne.ChildOne", "PageTwo")
whenRequestIsIssued("root", "type:pages")
thenResponseShouldBeXML()
}
public func testGetPageHierarchyHasRightTags() throws -> Void {
givenPages("PageOne", "PageOne.ChildOne", "PageTwo")
whenRequestIsIssued("root", "type:pages")
thenResponseShouldContain("<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>")
}์์์ ํจ์ ์ด๋ฆ์ ๋ฐ๊ฟ given-when-then์ด๋ผ๋ ๊ด๋ก๋ฅผ ์ฌ์ฉํ๋ค๋ ์ฌ์ค์ ์ฃผ๋ชฉํ๋ค. ๊ทธ๋ฌ๋ฉด ํ ์คํธ ์ฝ๋๋ฅผ ์ฝ๊ธฐ๊ฐ ์ฌ์์ง๋ค. ๋ถํํ๊ฒ๋, ์์์ ๋ณด๋ฏ์ด, ํ ์คํธ๋ฅผ ๋ถ๋ฆฌํ๋ฉด ์ค๋ณต๋๋ ์ฝ๋๊ฐ ๋ง์์ง๋ค.
TEMPLATE METHOD ํจํด์ ์ฌ์ฉํ๋ฉด ์ค๋ณต์ ์ ๊ฑฐํ ์ ์๋ค. given/when ๋ถ๋ถ์ ๋ถ๋ชจ ํด๋์ค์ ๋๊ณ then ๋ถ๋ถ์ ์์ ํด๋์ค์ ๋๋ฉด ๋๋ค. ์๋๋ฉด ์์ ํ ๋
์์ ์ธ ํ
์คํธ ํด๋์ค๋ฅผ ๋ง๋ค์ด @Before ํจ์์ given/when ๋ถ๋ถ์ ๋ฃ๊ณ @Test ํจ์์ then ๋ถ๋ถ์ ๋ฃ์ด๋ ๋๋ค. ํ์ง๋ง ๋ชจ๋๊ฐ ๋ฐฐ๋ณด๋ค ๋ฐฐ๊ผฝ์ด ๋ ํฌ๋ค. ์ด๊ฒ์ ๊ฒ ๊ฐ์ํด ๋ณด๋ฉด assert๋ฌธ์ ์ฌ๋ฟ ์ฌ์ฉํ๋ ํธ์ด ์ข๋ค๊ณ ์๊ฐํ๋ค.
'๋จ์ผ assert ๋ฌธ'์ด๋ผ๋ ๊ท์น์ด ํ๋ฅญํ ์ง์นจ์ด๋ผ ์๊ฐํ๋ค. ํ์ง๋ง ๋๋ก๋ ์ฃผ์ ์์ด ํจ์ ํ๋์ ์ฌ๋ฌ assert ๋ฌธ์ ๋ฃ๊ธฐ๋ ํ๋ค. ๋จ์ง assert ๋ฌธ ๊ฐ์๋ ์ต๋ํ ์ค์ฌ์ผ ์ข๋ค๋ ์๊ฐ์ด๋ค.
์ด์ฉ๋ฉด "ํ ์คํธ ํจ์๋ง๋ค ํ ๊ฐ๋ ๋ง ํ ์คํธํ๋ผ"๋ ๊ท์น์ด ๋ ๋ซ๊ฒ๋ค. ์ด๊ฒ์ ๊ฒ ์ก๋คํ ๊ฐ๋ ์ ์ฐ์์ผ๋ก ํ ์คํธํ๋ ๊น ํจ์๋ ํผํ๋ค.
// BAD
/**
* addMonth() ๋ฉ์๋๋ฅผ ํ
์คํธํ๋ ์ฅํฉํ ์ฝ๋
*/
public func testAddMonths() {
var d1: SerialDate = SerialDate.createInstance(31, 5, 2004);
var d2: SerialDate = SerialDate.addMonths(1, d1);
assertEquals(30, d2.getDayOfMonth());
assertEquals(6, d2.getMonth());
assertEquals(2004, d2.getYYYY());
var d3: SerialDate = SerialDate.addMonths(2, d1);
assertEquals(31, d3.getDayOfMonth());
assertEquals(7, d3.getMonth());
assertEquals(2004, d3.getYYYY());
var d4: SerialDate = SerialDate.addMonths(1, SerialDate.addMonths(1, d1));
assertEquals(30, d4.getDayOfMonth());
assertEquals(7, d4.getMonth());
assertEquals(2004, d4.getYYYY());
}ํ ์คํธ ํจ์๋ ๊ฐ๊ฐ ๋ค์ ๊ธฐ๋ฅ์ ์ํํ๋ค.
- (5์์ฒ๋ผ) 31๋ก ๋๋๋ ๋ฌ์ ๋ง์ง๋ง ๋ ์ง๊ฐ ์ฃผ์ด์ง๋ ๊ฒฝ์ฐ
- (6์์ฒ๋ผ) 30์ผ๋ก ๋๋๋ ํ ๋ฌ์ ๋ํ๋ฉด ๋ ์ง๋ 30์ผ์ด ๋์ด์ผ์ง 31์ผ์ด ๋์ด์๋ ์ ๋๋ค.
- ๋ ๋ฌ์ ๋ํ๋ฉด ๊ทธ๋ฆฌ๊ณ ๋ ๋ฒ์งธ ๋ฌ์ด 31๋ก ๋๋๋ฉด ๋ ์ง๋ 31์ผ์ด ๋์ด์ผ ํ๋ค.
- (6์์ฒ๋ผ) 30์ผ๋ก ๋๋๋ ๋ฌ์ ๋ง์ง๋ง ๋ ์ง๊ฐ ์ฃผ์ด์ง๋ ๊ฒฝ์ฐ
- 31์ผ๋ก ๋๋๋ ํ ๋ฌ์ ๋ํ๋ฉด ๋ ์ง๋ 30์ผ์ด ๋์ด์ผ์ง 31์ผ์ด ๋๋ฉด ์ ๋๋ค.
์ ์ฝ๋๋ ๊ฐ ์ ์ assert ๋ฌธ์ด ์ฌ๋ฟ์ด๋ผ๋ ์ฌ์ค์ด ๋ฌธ์ ๊ฐ ์๋๋ค. ํ ํ
์คํธ ํจ์์์ ์ฌ๋ฌ ๊ฐ๋
์ ํ
์คํธํ๋ค๋ ์ฌ์ค์ด ๋ฌธ์ ๋ค.
๊ฐ์ฅ ์ข์ ๊ท์น์ "๊ฐ๋ ๋น assert ๋ฌธ ์๋ฅผ ์ต์๋ก ์ค์ฌ๋ผ"์ "ํ ์คํธ ํจ์ ํ๋๋ ๊ฐ๋ ํ๋๋ง ํ ์คํธํ๋ผ"
๊นจ๋ํ ํ ์คํธ๋ ๋ค์ ๋ค์ฏ ๊ฐ์ง ๊ท์น์ ๋ฐ๋ฅด๋๋ฐ, ๊ฐ ๊ท์น์์ ์ฒซ ๊ธ์๋ฅผ ๋ฐ์ค๋ฉด FIRST๊ฐ ๋๋ค.
- ๋น ๋ฅด๊ฒFast: ํ ์คํธ๋ ๋นจ๋ผ์ผ ํ๋ค. ํ ์คํธ๋ ๋นจ๋ฆฌ ๋์์ผ ํ๋ค๋ ๋ง์ด๋ค. ํ ์คํธ๊ฐ ๋๋ฆฌ๋ฉด ์์ฃผ ๋๋ฆด ์๋๋ฅผ ๋ชป ๋ธ๋ค. ์์ฃผ ๋๋ฆฌ์ง ์์ผ๋ฉด ์ด๋ฐ์ ๋ฌธ์ ๋ฅผ ์ฐพ์๋ด ๊ณ ์น์ง ๋ชปํ๋ค. ์ฝ๋๋ฅผ ๋ง์๊ป ์ ๋ฆฌํ์ง๋ ๋ชปํ๋ค. ๊ฒฐ๊ตญ ์ฝ๋ ํ์ง์ด ๋ง๊ฐ์ง๊ธฐ ์์ํ๋ค.
- ๋ ๋ฆฝ์ ์ผ๋กIndependent: ๊ฐ ํ ์คํธ๋ ์๋ก ์์กดํ๋ฉด ์ ๋๋ค. ํ ํ ์คํธ๊ฐ ๋ค์ ํ ์คํธ๊ฐ ์คํ๋ ํ๊ฒฝ์ ์ค๋นํด์๋ ์ ๋๋ค. ๊ฐ ํ ์คํธ๋ ๋ ๋ฆฝ์ ์ผ๋ก ๊ทธ๋ฆฌ๊ณ ์ด๋ค ์์๋ก ์คํํด๋ ๊ด์ฐฎ์์ผ ํ๋ค. ํ ์คํธ๊ฐ ์๋ก์๊ฒ ์์กดํ๋ฉด ํ๋๊ฐ ์คํจํ ๋ ๋๋จธ์ง๋ ์๋ฌ์ ์คํจํ๋ฏ๋ก ์์ธ์ ์ง๋จํ๊ธฐ ์ด๋ ค์์ง๋ฉฐ ํ๋ฐ ํ ์คํธ๊ฐ ์ฐพ์๋ด์ผ ํ ๊ฒฐํจ์ด ์จ๊ฒจ์ง๋ค.
- ๋ฐ๋ณต๊ฐ๋ฅํ๊ฒRepeatable: ํ ์คํธ๋ ์ด๋ค ํ๊ฒฝ์์๋ ๋ฐ๋ณต ๊ฐ๋ฅํด์ผ ํ๋ค. ์ค์ ํ๊ฒฝ, QA ํ๊ฒฝ, ๋ฒ์ค๋ฅผ ํ๊ณ ์ง์ผ๋ก ๊ฐ๋ ๊ธธ์ ์ฌ์ฉํ๋ (๋คํธ์ํฌ์ ์ฐ๊ฒฐ๋์ง ์์) ๋ ธํธ๋ถ ํ๊ฒฝ์์๋ ์คํํ ์ ์์ด์ผ ํ๋ค. ํ ์คํธ๊ฐ ๋์๊ฐ์ง ์๋ ํ๊ฒฝ์ด ํ๋๋ผ๋ ์๋ค๋ฉด ํ ์คํธ๊ฐ ์คํจํ ์ด์ ๋ฅผ ๋๋ฌ๋ ๋ณ๋ช ์ด ์๊ธด๋ค. ๊ฒ๋ค๊ฐ ํ๊ฒฝ์ด ์ง์๋์ง ์๊ธฐ์ ํ ์คํธ๋ฅผ ์ํํ์ง ๋ชปํ๋ ์ํฉ์ ์ง๋ฉดํ๋ค.
- ์๊ฐ๊ฒ์ฆํ๋Self-Validating: ํ ์คํธ๋ ๋ถ์ธbool ๊ฐ์ผ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ด์ผ ํ๋ค. ์ฑ๊ณต ์๋๋ฉด ์คํจ๋ค. ํต๊ณผ ์ฌ๋ถ๋ฅผ ์๋ ค๊ณ ๋ก๊ทธ ํ์ผ์ ์ฝ๊ฒ ๋ง๋ค์ด์๋ ์ ๋๋ค. ํต๊ณผ ์ฌ๋ถ๋ฅผ ๋ณด๋ ค๊ณ ํ ์คํธ ํ์ผ ๋ ๊ฐ๋ฅผ ์์์ ์ผ๋ก ๋น๊ตํ๊ฒ ๋ง๋ค์ด์๋ ์ ๋๋ค. ํ ์คํธ๊ฐ ์ค์ค๋ก ์ฑ๊ณต๊ณผ ์คํจ๋ฅผ ๊ฐ๋ ํ์ง ์๋๋ค๋ฉด ํ๋จ์ ์ฃผ๊ด์ ์ด ๋๋ฉฐ ์ง๋ฃจํ ์์์ ํ๊ฐ๊ฐ ํ์ํ๊ฒ ๋๋ค.
- ์ ์์Timely: ํ ์คํธ๋ ์ ์์ ์์ฑํด์ผ ํ๋ค. ๋จ์ ํ ์คํธ๋ ํ ์คํธํ๋ ค๋ ์ค์ ์ฝ๋๋ฅผ ๊ตฌํํ๊ธฐ ์ง์ ์ ๊ตฌํํ๋ค. ์ค์ ์ฝ๋๋ฅผ ๊ตฌํํ ๋ค์์ ํ ์คํธ ์ฝ๋๋ฅผ ๋ง๋ค๋ฉด ์ค์ ์ฝ๋๊ฐ ํ ์คํธํ๊ธฐ ์ด๋ ต๋ค๋ ์ฌ์ค์ ๋ฐ๊ฒฌํ ์ง๋ ๋ชจ๋ฅธ๋ค. ์ด๋ค ์ค์ ์ฝ๋๋ ํ ์คํธํ๊ธฐ ๋๋ฌด ์ด๋ ต๋ค๊ณ ํ๋ช ๋ ์ง ๋ชจ๋ฅธ๋ค. ํ ์คํธ๊ฐ ๋ถ๊ฐ๋ฅํ๋๋ก ์ค์ ์ฝ๋๋ฅผ ์ค๊ณํ ์ง๋ ๋ชจ๋ฅธ๋ค.
ํ ์คํธ ์ฝ๋๋ ์ค์ ์ฝ๋๋งํผ์ด๋ ํ๋ก์ ํธ ๊ฑด๊ฐ์ ์ค์ํ๋ค. ์ด์ฉ๋ฉด ์ค์ ์ฝ๋๋ณด๋ค ๋ ์ค์ํ ์ง๋ ๋ชจ๋ฅด๊ฒ ๋ค. ํ ์คํธ ์ฝ๋๋ ์ค์ ์ฝ๋์ ์ ์ฐ์ฑ, ์ ์ง๋ณด์์ฑ, ์ฌ์ฌ์ฉ์ฑ์ ๋ณด์กดํ๊ณ ๊ฐํํ๊ธฐ ๋๋ฌธ์ด๋ค.
- ํ ์คํธ ์ฝ๋๋ ์ง์์ ์ผ๋ก ๊นจ๋ํ๊ฒ ๊ด๋ฆฌํ์.
- ํํ๋ ฅ์ ๋์ด๊ณ ๊ฐ๊ฒฐํ๊ฒ ์ ๋ฆฌํ์.
- ํ ์คํธ API๋ฅผ ๊ตฌํํด ๋๋ฉ์ธ ํนํ ์ธ์ดDomain Specific Language, DSL๋ฅผ ๋ง๋ค์.
- ํ ์คํธ ์ฝ๋๋ฅผ ๊นจ๋ํ๊ฒ ์ ์งํ์.