TDD – Java JUnit FizzBuzz

TDD – Java JUnit FizzBuzz

TDD - Java JUnit FizzBuzz

เรื่องนี้เกิดขึ้นในตอนเช้าวันอาทิตย์ ที่ผมกำลังฝึกใช้ FizzBuzz อยู่

ผมได้ยินมาว่า FizzBuzz จะถูกใช้ในการสัมภารณ์ด้วย ผมเลยว่าจะลองเล่นกับมันดูหน่อย

สามารถศึกษา FizzBuzz ได้ ที่นี้

วิดีโอที่ผมสร้างในส่วน TDD นั้นจะท้ายบทความครับ

อันดับ 1:

  • สร้างคลาสสำหรับการทดสอบ

  • คัดลอกกฎต่าง ๆ ตามช่องคอมเมนต์

  • รูปแบบของกฎทำให้มันเข้าใจง่าย

  • เพิ่มเติมตัวอย่างซักหน่อย ที่ทำให้เราเข้าใจได้

การทดสอบแรกของผม

การทดสอบแรกเขียนได้ดังนี้

@Test 
public void fizzBuzzConvertorLeavesNormalNumbersAlone(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter(); 
  Assert.assertEquals("1", fizzBuzz.convert(1)); 
}

ผมได้สร้างคลาส FizzBuzzConverter และวิธีการ convert

ผมเพิ่ม assertion ลำดับ 2 ในการทดสอบครั้งนี้ด้วย

Assert.assertEquals("2", fizzBuzz.convert(2));

ผมได้ทำการเปลี่ยนเป็นตามค่าดั้งเดิม

return String.valueOf(toConvertToFizzBuzz);

การทดสอบแรก

หลายคนอาจจะไม่ชอบ assertion หลายตัวในการทดสอบ

บางทีผมก็ทำ บางทีผมก็ไม่ทำ

นี้ไม่ใช่ความคิดผม

  • ชื่อที่ใช้ในการทดสอบอนุญาตให้ผมใช้ assertion หลายครั้ง
  • ผมคิดว่าวิธีการทดสอบหลายครั้งอาจจะทำให้มันยากขึ้น แทนที่จะใช้ Grok

การทดสอบครั้งที่สอง

ทดสองครั้งที่สองเป็น

@Test 
public void fizzBuzzConvertorMultiplesOfThree(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter(); 
  Assert.assertEquals("Fizz", fizzBuzz.convert(3)); 
}

นี่บังคับให้ผมต้องหาร 3 ให้ลงตัว

if(toConvertToFizzBuzz%3==0){ 
  return "Fizz"; 
}

ผมได้จินตนาการว่า  ถ้าคุณไม่รู้โมดูลทำงานอย่างไร  ทำให้ FizzBuzz สามารถเป็นไปค่อนข้างยาก

ผมได้เรียนรู้เบื้องหลังของโมดูลในวันนั้นแหละ  และลองทำโปรแกรม 8 บิท ขึ้นมา มีการทดลองเขียนหลายอย่าง clipping, และ scrolling routinesวนไปวนมาตั้งแต่นั้น

การทดสอบครั้งที่สาม

การทดสอบครั้งที่สามเป็น

@Test 
public void fizzBuzzConvertorMultiplesOfFive(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter();
  Assert.assertEquals("Buzz", fizzBuzz.convert(5)); 
}

ใช้เงื่อนไขเดียวกับตัวหาร 3 ลงตัว

if(toConvertToFizzBuzz%5==0){ 
  return "Buzz"; 
}

นี่เป็นวิธีการ convert ของผม สามารถดูได้เลยตามนี้

public String convert(int toConvertToFizzBuzz) { 
  if(toConvertToFizzBuzz%5==0){ 
    return "Buzz"; 
  } 
  if(toConvertToFizzBuzz%3==0){ 
    return "Fizz"; 
  } 
  return String.valueOf(toConvertToFizzBuzz); 
}

ผมได้เจอคนที่สร้างโค้ดที่ซับซ้อนสำหรับ FizzBuzz.

ผมยังคงยึดพื้นฐานที่แสนง่ายเหล่านั้นไว้อยู่ ถ้ามันทำงานได้ ผมก็แค่ refactor อย่างที่โปรแกรมเมอร์ดีๆควรจะทำ

การทดสอบครั้งที่ 4

การทดสอบครั้งที่สี่ก็ยังคงเหมือนกัน

@Test 
public void multiplesOfBothThreeAndFive(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter(); 
  Assert.assertEquals("FizzBuzz", fizzBuzz.convert(15)); 
}

ในจุดนี้ เรามาดูกันว่าผมคิดอย่างไรกับวิธีการแปลงนี้

  • ผมควรเพิ่ม flag เพื่อดู fizz และ buzz ดีไหม

  • ผมควรจะมี if else ซ้อนกันหลายชั้นดีไหม

  • บางทีผมใช้แค่ tertiary operator ก็พอ

ทันใดนั้น ผมตัดสินเขียนให้ง่าย

if(toConvertToFizzBuzz%15==0){ 
  return "FizzBuzz"; 
}

ความคิดของการทดสอบที่ 4

ผมสงสัยว่าจุดที่ทำให้คนใช้ FizzBuzz พลาดคือ โค้ดที่เขียนซับซ้อนเกินไป

หากดูวิธีการของผมไม่มีอะไรซับซ้อนเลย

public String convert(int toConvertToFizzBuzz) { 
  if(toConvertToFizzBuzz%15==0){ 
    return "FizzBuzz"; 
  } 
  if(toConvertToFizzBuzz%5==0){ 
    return "Buzz"; 
  } 
  if(toConvertToFizzBuzz%3==0){ 
    return "Fizz"; 
  } 
  return String.valueOf(toConvertToFizzBuzz); 
}

นี่เป็นเพียงการจัดลำดับว่าโค้ดไหนควรอยู่ตรงไหน

  • 15 สำคัญสุด เพราะว่ามันต้องทำการคูณ 3 กับ 5

  • 3 กับ 5 เท่ากับความสำคัญ และมันไม่ยังไม่รู้ว่ามันควรอยู่ตรงไหน

  • การเปลี่ยนค่าสตริงเป็นค่าเริ่มต้นดังนั้น จึงมีความสำคัญต่ำลงมา

วันวาน เราเคยถูกสอนว่าควร return แค่ค่าเดียวต่อ 1 method  ถ้าผมจะยืดแนวทางนี้ โค้ดผมจะยากเกินไปแทนที่ผมจะ

return

ตราบเท่าที่ตรงกับเงื่อนไข

ความจริงเป็นแล้ว เงื่อนไขเปรียบเสมือนตัวกรองไม่ให้ค่าที่เข้ามา คืนออกไปเป็นค่าเริ่มต้น

เสร็จแล้ว

ในที่สุดก็เสร็จสักที

แล้วในท้ายที่สุดผมมีอัลกอริทึมที่จะเปลี่ยนจำนวนเต็ม ในช่วง 1 ถึง 100 เป็น Fizz, Buzz, จำนวน หรือ FizzBuzz

ผมได้ใส่วงเล็บทั้งสองข้าง ด้วยบางอย่างที่ทำหน้าที่ แสดงผลค่านี้ออกไป

@Test 
public void outputTheHundredFizzBuzzes(){ 
  FizzBuzzConverter fizzBuzz = new FizzBuzzConverter(); 
  for(int i=1; i<=100; i++){ 
    System.out.println(fizzBuzz.convert(i)); 
  } 
}

ผมได้สร้าง @Test ขึ้นมา เพื่อความสะดวกต่อการรันโค้ดบน IDE

จากนั่นเทสเตอร์ (Tester) ก็เดินเข้ามา

  • ผมได้ใช้ TDD เพื่อออกแบบอัลกอริทีม

  • ผมไม่ได้ทดสอบค่า output เป็นกิจวัตร ผมได้ execute เพื่อดูผลลัพธ์จาก 1-100

  • ผมไม่ได้ยึดเกณฑ์อะไรมากมาย แค่ผมได้เห็นค่า output จาก outputTheHundredFizzBuzzes ตรงกับที่ผมต้องการ

ถ้านี้เป็นการสัมภาษณ์ ผมควรจะแปลง outputTheHundredFizzBuzzes เป็น main method แทน นี่เป็นการ implement แบบพื้นฐานให้ตรงตาม requirements ในฐานะเทสเตอร์ ผมไม่แน่ใจว่า ผมโน้มน้าวตัวเองว่า มันทำงานได้ หรือว่า มันทำงานได้จริงๆ คุณสามารถดูโค้ดทั้งหมดได้จากเรโปฯนี้

วีดีโอ

และที่สำคัญ

เตรียมพบกับหลักสูตร FULL TIME COURSE (JAVA)) เรียนกัน 3 เดือน

Codecamp

  1. คอร์สเรียนเข้มข้นจันทร์ – ศุกร์ เต็มวัน
  2. ฝึกงานด้วย Agile/Scrum ได้ทำงานจริงกับบริษัทจริง
  3. Speed Dating Day เลือกบริษัทที่ใช่กว่า 30 บริษัท

รายละเอียดเพิ่มเติม คลิกที่นี่