การทดสอบหน่วย .NET: ใช้จ่ายล่วงหน้าเพื่อบันทึกในภายหลัง

เผยแพร่แล้ว: 2022-03-11

มักจะมีความสับสนและสงสัยเกี่ยวกับการทดสอบหน่วยบ่อยครั้งเมื่อพูดคุยกับผู้มีส่วนได้ส่วนเสียและลูกค้า การทดสอบหน่วยในบางครั้งอาจฟังดูคล้ายกับการใช้ไหมขัดฟันกับเด็ก "ฉันแปรงฟันแล้ว ทำไมฉันต้องทำเช่นนี้"

การแนะนำการทดสอบหน่วยมักจะดูเหมือนเป็นค่าใช้จ่ายที่ไม่จำเป็นสำหรับผู้ที่พิจารณาวิธีการทดสอบและการทดสอบการยอมรับของผู้ใช้ที่แข็งแกร่งเพียงพอ

แต่การทดสอบหน่วยเป็นเครื่องมือที่ทรงพลังและง่ายกว่าที่คุณคิด ในบทความนี้ เราจะมาดูการทดสอบหน่วยและเครื่องมือที่มีอยู่ใน DotNet เช่น Microsoft.VisualStudio.TestTools และ Moq

เราจะพยายามสร้างไลบรารีคลาสอย่างง่ายที่จะคำนวณเทอมที่ n ในลำดับฟีโบนักชี ในการทำเช่นนั้น เราจะต้องสร้างคลาสสำหรับการคำนวณลำดับฟีโบนักชีที่ขึ้นอยู่กับคลาสคณิตศาสตร์แบบกำหนดเองที่บวกตัวเลขเข้าด้วยกัน จากนั้น เราสามารถใช้ .NET Testing Framework เพื่อให้แน่ใจว่าโปรแกรมของเราทำงานตามที่คาดไว้

การทดสอบหน่วยคืออะไร?

การทดสอบหน่วยจะแบ่งโปรแกรมออกเป็นโค้ดที่เล็กที่สุด โดยปกติแล้วจะเป็นระดับฟังก์ชัน และทำให้แน่ใจว่าฟังก์ชันจะส่งกลับค่าที่คาดไว้ ด้วยการใช้กรอบงานการทดสอบหน่วย การทดสอบหน่วยจะกลายเป็นเอนทิตีที่แยกจากกัน ซึ่งสามารถเรียกใช้การทดสอบอัตโนมัติในโปรแกรมในขณะที่กำลังถูกสร้างขึ้น

 [TestClass] public class FibonacciTests { [TestMethod] //Check the first value we calculate public void Fibonacci_GetNthTerm_Input2_AssertResult1() { //Arrange int n = 2; //setup Mock<UnitTests.IMath> mockMath = new Mock<UnitTests.IMath>(); mockMath .Setup(r => r.Add(It.IsAny<int>(), It.IsAny<int>())) .Returns((int x, int y) => x + y); UnitTests.Fibonacci fibonacci = new UnitTests.Fibonacci(mockMath.Object); //Act int result = fibonacci.GetNthTerm(n); //Assert Assert.AreEqual(result, 1); } }

การทดสอบหน่วยอย่างง่ายโดยใช้การทดสอบวิธี Arrange, Act, Assert ซึ่งห้องสมุดคณิตศาสตร์ของเราสามารถบวก 2 + 2 ได้อย่างถูกต้อง

เมื่อตั้งค่าการทดสอบหน่วยแล้ว หากมีการเปลี่ยนแปลงโค้ด ให้พิจารณาเงื่อนไขเพิ่มเติมที่ไม่ทราบว่าโปรแกรมได้รับการพัฒนาครั้งแรกเมื่อใด เช่น การทดสอบหน่วยจะแสดงว่าทุกกรณีตรงกับค่าที่คาดไว้ เอาต์พุตโดยฟังก์ชัน

การทดสอบหน่วย ไม่ใช่ การทดสอบการรวม ไม่ใช่ การทดสอบแบบ end-to-end แม้ว่าทั้งสองวิธีนี้จะเป็นวิธีการที่มีประสิทธิภาพ แต่ก็ควรทำงานร่วมกับการทดสอบหน่วย ไม่ใช่เพื่อทดแทน

ประโยชน์และวัตถุประสงค์ของการทดสอบหน่วย

ประโยชน์ที่ยากที่สุดของการทดสอบหน่วยในการทำความเข้าใจ แต่ที่สำคัญที่สุดคือความสามารถในการทดสอบโค้ดที่เปลี่ยนแปลงซ้ำได้ทันที เหตุผลที่เข้าใจยากนักก็เพราะว่านักพัฒนาหลายคนคิดกับตัวเองว่า "ฉันจะไม่แตะต้องฟังก์ชันนั้นอีก" หรือ "ฉันจะทดสอบอีกครั้งเมื่อทำเสร็จแล้ว" และผู้มีส่วนได้ส่วนเสียคิดในแง่ ที่ว่า “ถ้างานชิ้นนั้นเขียนแล้ว ทำไมฉันต้องสอบใหม่”

ในฐานะที่เป็นคนที่อยู่สองด้านของสเปกตรัมการพัฒนา ฉันได้พูดทั้งสองสิ่งนี้แล้ว นักพัฒนาที่อยู่ในตัวฉันรู้ว่าทำไมเราต้องทดสอบใหม่

การเปลี่ยนแปลงที่เราทำในแต่ละวันอาจมีผลกระทบมหาศาล ตัวอย่างเช่น:

  • สวิตช์ของคุณพิจารณาค่าใหม่ที่คุณใส่ไว้อย่างถูกต้องหรือไม่?
  • คุณรู้ไหมว่าคุณใช้สวิตช์นั้นกี่ครั้ง?
  • คุณพิจารณาการเปรียบเทียบสตริงที่ไม่คำนึงถึงขนาดตัวพิมพ์อย่างถูกต้องหรือไม่
  • คุณกำลังตรวจสอบค่า null อย่างเหมาะสมหรือไม่?
  • ข้อยกเว้นการโยนได้รับการจัดการตามที่คุณคาดไว้หรือไม่?

การทดสอบหน่วยใช้คำถามเหล่านี้และบันทึกเป็นรหัสและ กระบวนการ เพื่อให้แน่ใจว่าคำถามเหล่านี้จะได้รับคำตอบเสมอ สามารถเรียกใช้การทดสอบหน่วยก่อนการสร้างเพื่อให้แน่ใจว่าคุณไม่ได้แนะนำจุดบกพร่องใหม่ เนื่องจากการทดสอบหน่วยได้รับการออกแบบให้เป็นปรมาณู การทดสอบจึงดำเนินการเร็วมาก โดยปกติแล้วจะน้อยกว่า 10 มิลลิวินาทีต่อการทดสอบ แม้ในแอปพลิเคชันขนาดใหญ่มาก ก็สามารถดำเนินการชุดทดสอบเต็มรูปแบบได้ภายในเวลาไม่ถึงชั่วโมง กระบวนการ UAT ของคุณสามารถจับคู่ได้หรือไม่

ตัวอย่างการตั้งชื่อแบบแผนการตั้งชื่อเพื่อให้ค้นหาคลาสหรือเมธอดภายในคลาสที่ต้องการทดสอบได้อย่างง่ายดาย
นอกเหนือจาก Fibonacci_GetNthTerm_Input2_AssertResult1 ซึ่งเป็นการรันครั้งแรกและรวมถึงเวลาการตั้งค่า การทดสอบหน่วยทั้งหมดทำงานภายใต้ 5 มิลลิวินาที แบบแผนการตั้งชื่อของฉันที่นี่ถูกตั้งค่าให้ค้นหาคลาสหรือเมธอดภายในคลาสที่ฉันต้องการทดสอบได้อย่างง่ายดาย

ในฐานะนักพัฒนาซอฟต์แวร์ บางทีนี่อาจฟังดูเหมาะกับคุณมากกว่า ใช่ คุณสบายใจได้ว่ารหัสที่คุณเผยแพร่นั้นดี แต่การทดสอบหน่วยยังเปิดโอกาสให้คุณได้เห็นว่าการออกแบบของคุณมีจุดอ่อนตรงไหน คุณกำลังเขียนการทดสอบหน่วยเดียวกันสำหรับโค้ดสองชิ้นหรือไม่? พวกเขาควรอยู่ในโค้ดชิ้นเดียวแทนหรือไม่

การทำให้โค้ดของคุณเป็นแบบทดสอบหน่วยได้เป็นวิธีหนึ่งในการปรับปรุงการออกแบบของคุณ และสำหรับนักพัฒนาซอฟต์แวร์ส่วนใหญ่ที่ไม่เคยทดสอบหน่วย หรือไม่ใช้เวลามากในการพิจารณาการออกแบบก่อนเขียนโค้ด คุณจะทราบได้ว่าการออกแบบของคุณพัฒนาขึ้นมากเพียงใดโดยเตรียมให้พร้อมสำหรับการทดสอบหน่วย

หน่วยรหัสของคุณสามารถทดสอบได้หรือไม่?

นอกจาก DRY แล้ว เรายังมีข้อควรพิจารณาอื่นๆ ด้วย

วิธีการหรือหน้าที่ของคุณพยายามทำมากเกินไปหรือไม่?

หากคุณต้องการเขียนการทดสอบหน่วยที่ซับซ้อนมากเกินไปซึ่งใช้เวลานานกว่าที่คุณคาดไว้ วิธีการของคุณอาจซับซ้อนเกินไปและเหมาะสมกว่าสำหรับวิธีการหลายวิธี

คุณใช้ประโยชน์จากการพึ่งพาการฉีดอย่างเหมาะสมหรือไม่?

หากวิธีการของคุณภายใต้การทดสอบต้องการคลาสหรือฟังก์ชันอื่น เราเรียกสิ่งนี้ว่าการพึ่งพา ในการทดสอบหน่วย เราไม่สนใจว่าการพึ่งพากำลังทำอะไรภายใต้ประทุน สำหรับวิธีการที่ทดสอบนั้นเป็นกล่องดำ การพึ่งพาอาศัยกันมีชุดการทดสอบหน่วยของตัวเองซึ่งจะกำหนดว่าพฤติกรรมทำงานอย่างถูกต้องหรือไม่

ในฐานะผู้ทดสอบ คุณต้องการจำลองการขึ้นต่อกันนั้นและบอกว่าค่าใดที่จะส่งคืนในอินสแตนซ์เฉพาะ สิ่งนี้จะช่วยให้คุณควบคุมกรณีทดสอบของคุณได้ดียิ่งขึ้น ในการทำเช่นนี้ คุณจะต้องฉีดเวอร์ชันจำลอง (หรือที่เราจะเห็นในภายหลัง ล้อเลียน) ของการพึ่งพานั้น

ส่วนประกอบของคุณโต้ตอบกันอย่างที่คุณคาดหวังหรือไม่?

เมื่อคุณคำนวณการพึ่งพาและการฉีดการพึ่งพาของคุณแล้ว คุณอาจพบว่าคุณได้แนะนำการพึ่งพาแบบวนซ้ำในโค้ดของคุณ หากคลาส A ขึ้นอยู่กับคลาส B ซึ่งจะขึ้นอยู่กับคลาส A คุณควรพิจารณาการออกแบบของคุณใหม่

ความงามของการฉีดพึ่งพา

ลองพิจารณาตัวอย่างฟีโบนักชีของเรา หัวหน้าของคุณบอกคุณว่าพวกเขามีคลาสใหม่ที่มีประสิทธิภาพและแม่นยำกว่าตัวดำเนินการเพิ่มปัจจุบันที่มีอยู่ใน C#

แม้ว่าตัวอย่างนี้จะไม่เกิดขึ้นจริงในโลกแห่งความเป็นจริง แต่เราได้เห็นตัวอย่างที่คล้ายคลึงกันในองค์ประกอบอื่นๆ เช่น การรับรองความถูกต้อง การแมปวัตถุ และกระบวนการอัลกอริธึมเกือบทุกอย่าง สำหรับจุดประสงค์ของบทความนี้ สมมุติว่าฟังก์ชัน add ใหม่ของไคลเอ็นต์ของคุณเป็นฟังก์ชันใหม่ล่าสุดและดีที่สุดนับตั้งแต่มีการประดิษฐ์คอมพิวเตอร์ขึ้นมา

ด้วยเหตุนี้ เจ้านายของคุณจึงมอบไลบรารีกล่องดำที่มีคลาส Math หนึ่งคลาส และในคลาสนั้น ฟังก์ชั่นเดียว Add งานของคุณในการใช้เครื่องคิดเลข Fibonacci มีแนวโน้มที่จะมีลักษณะดังนี้:

 public int GetNthTerm(int n) { Math math = new Math(); int nMinusTwoTerm = 1; int nMinusOneTerm = 1; int newTerm = 0; for (int i = 2; i < n; i++) { newTerm = math.Add(nMinusOneTerm, nMinusTwoTerm); nMinusTwoTerm = nMinusOneTerm; nMinusOneTerm = newTerm; } return newTerm; }

นี้ไม่ได้น่ากลัว คุณสร้างอินสแตนซ์ของชั้นเรียน Math ใหม่และใช้สิ่งนั้นเพื่อเพิ่มคำศัพท์สองคำก่อนหน้าเพื่อรับถัดไป คุณใช้วิธีนี้ผ่านชุดทดสอบปกติของคุณ คำนวณถึง 100 เทอม คำนวณเทอมที่ 1000 เทอมที่ 10,000 และอื่นๆ จนกว่าคุณจะรู้สึกพอใจว่าวิธีการของคุณใช้ได้ดี ในอนาคต ผู้ใช้รายหนึ่งบ่นว่าคำที่ 501 ไม่ทำงานตามที่คาดไว้ คุณใช้เวลาช่วงเย็นอ่านโค้ดของคุณ และพยายามหาสาเหตุที่กรณีมุมนี้ใช้ไม่ได้ผล คุณเริ่มสงสัยว่าชั้นเรียน Math ล่าสุดและยิ่งใหญ่ที่สุดนั้นไม่ได้ยอดเยี่ยมอย่างที่เจ้านายของคุณคิด แต่มันเป็นกล่องดำ และคุณไม่สามารถพิสูจน์ได้จริงๆ ว่าคุณเข้าถึงทางตันภายใน

ปัญหาคือว่า Math การพึ่งพาไม่ได้ถูกฉีดเข้าไปในเครื่องคำนวณฟีโบนักชีของคุณ ดังนั้น ในการทดสอบของคุณ คุณจะต้องพึ่งพาผลลัพธ์ที่มีอยู่ ยังไม่ทดสอบ และไม่รู้จักจาก Math เพื่อทดสอบฟีโบนักชีด้วย หากมีปัญหากับ Math แสดงว่า Fibonacci จะผิดเสมอ (โดยไม่ต้องเขียนโค้ดกรณีพิเศษสำหรับเทอมที่ 501)

แนวคิดในการแก้ไขปัญหานี้คือการเพิ่มคลาส Math ลงในเครื่องคำนวณฟีโบนักชีของคุณ แต่ที่ดียิ่งกว่านั้นคือการสร้างอินเทอร์เฟซสำหรับคลาส Math ที่กำหนดวิธีการสาธารณะ (ในกรณีของเราคือ Add ) และใช้อินเทอร์เฟซในคลาส Math ของเรา

 public interface IMath { int Add(int x, int y); } public class Math : IMath { public int Add(int x, int y) { //super secret implementation here } } }

แทนที่จะใส่คลาส Math ลงใน Fibonacci เราสามารถแทรกอินเทอร์เฟซ IMath ลงใน Fibonacci ได้ ประโยชน์ที่นี่คือเราสามารถกำหนดคลาส OurMath ของเราเองซึ่งเรารู้ว่าถูกต้องและทดสอบเครื่องคิดเลขของเรากับสิ่งนั้น ยิ่งไปกว่านั้น เมื่อใช้ Moq เราสามารถกำหนดได้ว่าผลตอบแทนของ Math.Add ใด เราสามารถกำหนดจำนวนรวมหรือบอก Math.Add ให้คืนค่า x + y ก็ได้

 private IMath _math; public Fibonacci(IMath math) { _math = math; }

แทรกอินเทอร์เฟซ IMath ลงในคลาส Fibonacci

 //setup Mock<UnitTests.IMath> mockMath = new Mock<UnitTests.IMath>(); mockMath .Setup(r => r.Add(It.IsAny<int>(), It.IsAny<int>())) .Returns((int x, int y) => x + y);

ใช้ Moq เพื่อกำหนดผลตอบแทนของ Math.Add

ตอนนี้เรามีวิธีทดลองและจริง (ถ้าตัวดำเนินการ + นั้นผิดใน C # เรามีปัญหาที่ใหญ่กว่า) สำหรับการเพิ่มตัวเลขสองตัว ด้วยการใช้ IMath ใหม่ของเรา เราสามารถเขียนโค้ดการทดสอบหน่วยสำหรับเทอมที่ 501 ของเรา และดูว่าเราได้บิดเบือนการใช้งานของเราหรือไม่ หรือหากคลาส Math แบบกำหนดเองต้องการงานเพิ่มขึ้นอีกเล็กน้อย

อย่าให้วิธีการพยายามทำมากเกินไป

ตัวอย่างนี้ยังชี้ให้เห็นถึงแนวคิดของวิธีการที่ทำมากเกินไป แน่นอนว่าการเพิ่มนั้นเป็นการดำเนินการที่ค่อนข้างง่ายโดยไม่จำเป็นต้องแยกฟังก์ชันการทำงานออกจากเมธอด GetNthTerm ของเรามากนัก แต่ถ้าการดำเนินการซับซ้อนกว่านี้เล็กน้อยล่ะ แทนที่จะเป็นการเพิ่ม บางทีอาจเป็นการตรวจสอบโมเดล การเรียกโรงงานเพื่อขอรับอ็อบเจ็กต์เพื่อดำเนินการ หรือรวบรวมข้อมูลที่จำเป็นเพิ่มเติมจากที่เก็บ

นักพัฒนาส่วนใหญ่จะพยายามยึดแนวคิดว่าวิธีหนึ่งมีจุดประสงค์เดียว ในการทดสอบหน่วย เราพยายามยึดหลักการที่ว่าการทดสอบหน่วยควรนำไปใช้กับวิธีอะตอมมิกและโดยการแนะนำการดำเนินการมากเกินไปกับวิธีการที่ทำให้เราไม่สามารถทดสอบได้ เรามักจะสร้างปัญหาที่เราต้องเขียนการทดสอบจำนวนมากเพื่อทดสอบฟังก์ชันของเราอย่างเหมาะสม

พารามิเตอร์แต่ละตัวที่เราเพิ่มลงในวิธีการจะเพิ่มจำนวนการทดสอบที่เราต้องเขียนแบบทวีคูณตามความซับซ้อนของพารามิเตอร์ หากคุณเพิ่มบูลีนให้กับลอจิก คุณต้องเพิ่มจำนวนการทดสอบเป็นสองเท่าเพื่อเขียน เนื่องจากตอนนี้คุณต้องตรวจสอบกรณีจริงและเท็จพร้อมกับการทดสอบปัจจุบันของคุณ ในกรณีของการตรวจสอบแบบจำลอง ความซับซ้อนของการทดสอบหน่วยของเราสามารถเพิ่มขึ้นอย่างรวดเร็ว

แผนภาพของการทดสอบที่เพิ่มขึ้นซึ่งจำเป็นเมื่อเพิ่มบูลีนเข้ากับตรรกะ

เราทุกคนมีความผิดในการเพิ่มความพิเศษเล็กน้อยให้กับวิธีการ แต่วิธีการที่ใหญ่กว่าและซับซ้อนกว่าเหล่านี้สร้างความจำเป็นในการทดสอบหน่วยมากเกินไป และจะเห็นได้อย่างรวดเร็วเมื่อคุณเขียนการทดสอบหน่วยว่าวิธีการพยายามทำมากเกินไป หากคุณรู้สึกว่าคุณกำลังพยายามทดสอบผลลัพธ์ที่เป็นไปได้มากเกินไปจากพารามิเตอร์อินพุตของคุณ ให้พิจารณาว่าวิธีการของคุณต้องแบ่งออกเป็นชุดย่อยที่เล็กกว่า

อย่าซ้ำเติมตัวเอง

หนึ่งในผู้เช่าโปรแกรมที่เราชื่นชอบ อันนี้ควรจะค่อนข้างตรงไปตรงมา หากคุณพบว่าตัวเองเขียนแบบทดสอบเดียวกันมากกว่าหนึ่งครั้ง แสดงว่าคุณได้แนะนำโค้ดมากกว่าหนึ่งครั้ง อาจเป็นประโยชน์กับคุณในการปรับโครงสร้างใหม่ที่ทำงานในคลาสทั่วไปที่สามารถเข้าถึงได้จากทั้งสองอินสแตนซ์ที่คุณพยายามจะใช้

มีเครื่องมือทดสอบหน่วยใดบ้าง

DotNet เสนอแพลตฟอร์มการทดสอบหน่วยที่มีประสิทธิภาพมากให้กับเราตั้งแต่แกะกล่อง คุณสามารถใช้วิธีการที่เรียกว่า Arrange, Act, Assert methodology ได้ คุณจัดเตรียมการพิจารณาเบื้องต้นของคุณ ปฏิบัติตามเงื่อนไขเหล่านั้นด้วยวิธีการของคุณภายใต้การทดสอบ จากนั้นยืนยันสิ่งที่เกิดขึ้น คุณสามารถยืนยันอะไรก็ได้ ทำให้เครื่องมือนี้มีประสิทธิภาพมากยิ่งขึ้น คุณสามารถยืนยันได้ว่าวิธีการนั้นถูกเรียกเป็นจำนวนครั้งที่เจาะจง วิธีการนั้นคืนค่าเฉพาะ ข้อยกเว้นประเภทใดประเภทหนึ่งถูกส่งออกไป หรืออย่างอื่นที่คุณคิดได้ สำหรับผู้ที่มองหาเฟรมเวิร์กขั้นสูง NUnit และ JUnit ที่เทียบเท่ากับ Java เป็นตัวเลือกที่ทำงานได้

 [TestMethod] //Test To Verify Add Never Called on the First Term public void Fibonacci_GetNthTerm_Input0_AssertAddNeverCalled() { //Arrange int n = 0; //setup Mock<UnitTests.IMath> mockMath = new Mock<UnitTests.IMath>(); mockMath .Setup(r => r.Add(It.IsAny<int>(), It.IsAny<int>())) .Returns((int x, int y) => x + y); UnitTests.Fibonacci fibonacci = new UnitTests.Fibonacci(mockMath.Object); //Act int result = fibonacci.GetNthTerm(n); //Assert mockMath.Verify(r => r.Add(It.IsAny<int>(), It.IsAny<int>()), Times.Never); }

การทดสอบว่าวิธี Fibonacci ของเราจัดการกับตัวเลขติดลบโดยการโยนข้อยกเว้น การทดสอบหน่วยสามารถตรวจสอบว่ามีข้อยกเว้นเกิดขึ้น

ในการจัดการการพึ่งพาการฉีด ทั้ง Ninject และ Unity มีอยู่บนแพลตฟอร์ม DotNet ทั้งสองมีความแตกต่างกันน้อยมาก และกลายเป็นเรื่องว่าคุณต้องการจัดการการกำหนดค่าด้วย Fluent Syntax หรือ XML Configuration

สำหรับการจำลองการพึ่งพา ฉันแนะนำ Moq ขั้นต่ำอาจเป็นเรื่องยากที่จะรับมือ แต่ประเด็นสำคัญคือคุณสร้างเวอร์ชันจำลองของการพึ่งพาของคุณ จากนั้น คุณบอกผู้ขึ้นต่อกันว่าจะส่งคืนอะไรภายใต้เงื่อนไขเฉพาะ ตัวอย่างเช่น หากคุณมีเมธอดชื่อ Square(int x) ที่ยกกำลังสองจำนวนเต็ม คุณสามารถบอกได้เมื่อ x = 2 คืนค่า 4 คุณยังสามารถบอกให้คืนค่า x^2 สำหรับจำนวนเต็มใดๆ ก็ได้ หรือคุณอาจบอกให้มันคืนค่า 5 เมื่อ x = 2 ทำไมคุณถึงทำกรณีสุดท้าย ในกรณีที่วิธีการภายใต้บทบาทของการทดสอบคือการตรวจสอบคำตอบจากการขึ้นต่อกัน คุณอาจต้องการบังคับให้คำตอบที่ไม่ถูกต้องกลับมาเพื่อให้แน่ใจว่าคุณจับจุดบกพร่องได้อย่างเหมาะสม

 [TestMethod] //Test To Verify Add Called Three times on the fifth Term public void Fibonacci_GetNthTerm_Input4_AssertAddCalledThreeTimes() { //Arrange int n = 4; //setup Mock<UnitTests.IMath> mockMath = new Mock<UnitTests.IMath>(); mockMath .Setup(r => r.Add(It.IsAny<int>(), It.IsAny<int>())) .Returns((int x, int y) => x + y); UnitTests.Fibonacci fibonacci = new UnitTests.Fibonacci(mockMath.Object); //Act int result = fibonacci.GetNthTerm(n); //Assert mockMath.Verify(r => r.Add(It.IsAny<int>(), It.IsAny<int>()), Times.Exactly(3)); }

ใช้ Moq เพื่อบอกอินเทอร์เฟซ IMath ที่เยาะเย้ยถึงวิธีจัดการ Add ภายใต้การทดสอบ คุณสามารถกำหนดกรณีที่ชัดเจนด้วย It.Is หรือช่วงด้วย It.IsInRange

กรอบงานการทดสอบหน่วยสำหรับ DotNet

กรอบงานการทดสอบหน่วยของ Microsoft

Microsoft Unit Testing Framework เป็นโซลูชันการทดสอบหน่วยแบบสำเร็จรูปจาก Microsoft และรวมอยู่ใน Visual Studio เพราะมันมาพร้อมกับ VS มันจึงเข้ากันได้ดีกับมัน เมื่อคุณเริ่มโครงการ Visual Studio จะถามคุณว่าคุณต้องการสร้าง Unit Test Library ควบคู่ไปกับแอปพลิเคชันของคุณหรือไม่

Microsoft Unit Testing Framework ยังมาพร้อมกับเครื่องมือมากมายที่จะช่วยให้คุณวิเคราะห์ขั้นตอนการทดสอบได้ดียิ่งขึ้น นอกจากนี้ เนื่องจาก Microsoft เป็นเจ้าของและเขียนขึ้น จึงมีความรู้สึกถึงความมั่นคงในการดำรงอยู่ในอนาคต

แต่เมื่อทำงานกับเครื่องมือของ Microsoft คุณจะได้รับสิ่งที่พวกเขามอบให้ Microsoft Unit Testing Framework อาจมีความยุ่งยากในการผสานรวม

NUnit

ข้อดีที่ใหญ่ที่สุดสำหรับฉันในการใช้ NUnit คือการทดสอบแบบกำหนดพารามิเตอร์ ในตัวอย่าง Fibonacci ด้านบนของเรา เราสามารถป้อนกรณีทดสอบจำนวนหนึ่งและรับรองว่าผลลัพธ์เหล่านั้นเป็นจริง และในกรณีของปัญหาที่ 501 เราสามารถเพิ่มชุดพารามิเตอร์ใหม่ได้เสมอ เพื่อให้แน่ใจว่าการทดสอบจะทำงานอยู่เสมอโดยไม่จำเป็นต้องใช้วิธีทดสอบใหม่

ข้อเสียเปรียบหลักของ NUnit คือการรวมเข้ากับ Visual Studio ไม่มีเสียงระฆังและเสียงนกหวีดที่มาพร้อมกับเวอร์ชัน Microsoft และหมายความว่าคุณจะต้องดาวน์โหลดชุดเครื่องมือของคุณเอง

xUnit.Net

xUnit เป็นที่นิยมอย่างมากใน C# เพราะมันรวมเข้ากับระบบนิเวศ .NET ที่มีอยู่ได้เป็นอย่างดี Nuget มีส่วนขยาย xUnit มากมาย นอกจากนี้ยังรวมเข้ากับ Team Foundation Server ได้เป็นอย่างดี แม้ว่าฉันจะไม่แน่ใจว่านักพัฒนา .NET กี่คนยังคงใช้ TFS กับการใช้งาน Git ที่หลากหลาย

ข้อเสีย ผู้ใช้หลายคนบ่นว่าเอกสารของ xUnit ขาดไปเล็กน้อย สำหรับผู้ใช้ใหม่ในการทดสอบหน่วย การทำเช่นนี้อาจทำให้ปวดหัวได้มาก นอกจากนี้ ความสามารถในการขยายและความสามารถในการปรับตัวของ xUnit ยังทำให้เส้นโค้งการเรียนรู้มีความชันกว่าเล็กน้อยเมื่อเทียบกับ NUnit หรือ Unit Testing Framework ของ Microsoft

การออกแบบ/การพัฒนาที่ขับเคลื่อนด้วยการทดสอบ

การออกแบบ/การพัฒนาที่ขับเคลื่อนด้วยการทดสอบ (TDD) เป็นหัวข้อขั้นสูงอีกเล็กน้อยที่สมควรได้รับการโพสต์ของตัวเอง อย่างไรก็ตาม ฉันอยากจะแนะนำ

แนวคิดคือการเริ่มต้นด้วยการทดสอบหน่วยของคุณและบอกการทดสอบหน่วยของคุณว่าอะไรถูกต้อง จากนั้นคุณสามารถเขียนโค้ดของคุณเกี่ยวกับการทดสอบเหล่านั้น ตามทฤษฎีแล้ว แนวคิดนี้ฟังดูง่าย แต่ในทางปฏิบัติ การฝึกสมองให้คิดย้อนหลังเกี่ยวกับแอปพลิเคชันนั้นยากมาก แต่วิธีการนี้มีประโยชน์ในตัวที่ไม่ต้องเขียนการทดสอบหน่วยหลังจากข้อเท็จจริง สิ่งนี้นำไปสู่การจัดโครงสร้างใหม่ การเขียนใหม่ และความสับสนในชั้นเรียนน้อยลง

TDD เป็นคำศัพท์เล็กน้อยในช่วงไม่กี่ปีที่ผ่านมา แต่การรับเลี้ยงบุตรบุญธรรมนั้นช้า ลักษณะทางความคิดสร้างความสับสนให้กับผู้มีส่วนได้ส่วนเสียซึ่งทำให้ยากต่อการได้รับการอนุมัติ แต่ในฐานะนักพัฒนา เราขอแนะนำให้คุณเขียนแอปพลิเคชันขนาดเล็กโดยใช้แนวทาง TDD เพื่อทำความคุ้นเคยกับกระบวนการ

ทำไมคุณไม่มีการทดสอบหน่วยมากเกินไป

การทดสอบหน่วยเป็นหนึ่งในเครื่องมือทดสอบที่ทรงพลังที่สุดที่นักพัฒนาซอฟต์แวร์มี ไม่เพียงพอสำหรับการทดสอบแอปพลิเคชันของคุณอย่างเต็มรูปแบบ แต่ประโยชน์ของมันในการทดสอบการถดถอย การออกแบบโค้ด และเอกสารประกอบของวัตถุประสงค์นั้นไม่มีใครเทียบได้

ไม่มีสิ่งที่เรียกว่าการเขียนแบบทดสอบหน่วยมากเกินไป แต่ละกรณีขอบสามารถเสนอปัญหาใหญ่ในซอฟต์แวร์ของคุณ การจดจำจุดบกพร่องที่ตรวจพบขณะทำการทดสอบหน่วยสามารถรับประกันได้ว่าจุดบกพร่องเหล่านั้นจะไม่พบวิธีที่จะย้อนกลับเข้ามาในซอฟต์แวร์ของคุณในระหว่างการเปลี่ยนแปลงโค้ดในภายหลัง แม้ว่าคุณสามารถเพิ่มงบประมาณล่วงหน้าของโครงการได้ 10-20% แต่คุณสามารถประหยัดได้มากกว่านั้นในการฝึกอบรม การแก้ไขจุดบกพร่อง และเอกสารประกอบ

คุณสามารถค้นหาที่เก็บ Bitbucket ที่ใช้ในบทความนี้ได้ที่นี่