ทำสำเนาค่าคงที่ระหว่างการทดสอบและรหัสการผลิตหรือไม่


20

การทำซ้ำข้อมูลระหว่างการทดสอบและรหัสจริงดีหรือไม่ดี ตัวอย่างเช่นสมมติว่าฉันมีคลาส Python FooSaverที่บันทึกไฟล์ด้วยชื่อเฉพาะลงในไดเรกทอรีที่กำหนด:

class FooSaver(object):
  def __init__(self, out_dir):
    self.out_dir = out_dir

  def _save_foo_named(self, type_, name):
    to_save = None
    if type_ == FOOTYPE_A:
      to_save = make_footype_a()
    elif type == FOOTYPE_B:
      to_save = make_footype_b()
    # etc, repeated
    with open(self.out_dir + name, "w") as f:
      f.write(str(to_save))

  def save_type_a(self):
    self._save_foo_named(a, "a.foo_file")

  def save_type_b(self):
    self._save_foo_named(b, "b.foo_file")

ตอนนี้ในการทดสอบของฉันฉันต้องการให้แน่ใจว่าไฟล์เหล่านี้ทั้งหมดถูกสร้างขึ้นดังนั้นฉันต้องการพูดแบบนี้:

foo = FooSaver("/tmp/special_name")
foo.save_type_a()
foo.save_type_b()

self.assertTrue(os.path.isfile("/tmp/special_name/a.foo_file"))
self.assertTrue(os.path.isfile("/tmp/special_name/b.foo_file"))

แม้ว่าสิ่งนี้จะซ้ำกับชื่อไฟล์ในสองแห่ง แต่ฉันคิดว่ามันดี: มันบังคับให้ฉันเขียนสิ่งที่ฉันคาดหวังว่าจะออกมาจากปลายอีกด้านหนึ่งมันเพิ่มชั้นของการป้องกันการพิมพ์ผิดและโดยทั่วไปทำให้ฉันรู้สึกมั่นใจว่าสิ่งต่างๆ อย่างที่ฉันคาดไว้ ฉันรู้ว่าถ้าฉันเปลี่ยนa.foo_fileไปtype_a.foo_fileในอนาคตฉันจะต้องทำการค้นหาและแทนที่ในการทดสอบของฉัน แต่ฉันไม่คิดว่ามันเป็นเรื่องใหญ่เกินไป ฉันควรจะมีผลบวกที่ผิดพลาดหากฉันลืมอัปเดตการทดสอบเพื่อแลกเปลี่ยนเพื่อให้แน่ใจว่าการเข้าใจรหัสและการทดสอบของฉันตรงกัน

เพื่อนร่วมงานคิดว่าการทำซ้ำนี้ไม่ดีและแนะนำให้ฉันปรับโครงสร้างทั้งสองด้านเป็นดังนี้:

class FooSaver(object):
  A_FILENAME = "a.foo_file"
  B_FILENAME = "b.foo_file"

  # as before...

  def save_type_a(self):
    self._save_foo_named(a, self.A_FILENAME)

  def save_type_b(self):
    self._save_foo_named(b, self.B_FILENAME)

และในการทดสอบ:

self.assertTrue(os.path.isfile("/tmp/special_name/" + FooSaver.A_FILENAME))
self.assertTrue(os.path.isfile("/tmp/special_name/" + FooSaver.B_FILENAME))

ฉันไม่ชอบสิ่งนี้เพราะมันไม่ทำให้ฉันมั่นใจว่ารหัสกำลังทำสิ่งที่ฉันคาดไว้ --- ฉันเพิ่งทำซ้ำout_dir + nameขั้นตอนทั้งด้านการผลิตและด้านการทดสอบ มันจะไม่เปิดเผยข้อผิดพลาดในความเข้าใจของฉันเกี่ยวกับวิธีการ+ทำงานของสายอักขระและจะไม่ตรวจจับความผิดพลาด

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

มีแบบอย่างที่ชัดเจนที่นี่? การทำซ้ำค่าคงที่ตลอดการทดสอบและรหัสการผลิตหรือไม่หรือว่าเปราะเกินไป?

คำตอบ:


16

ฉันคิดว่ามันขึ้นอยู่กับสิ่งที่คุณพยายามทดสอบซึ่งไปตามสัญญาของชั้นเรียน

หากสัญญาของชั้นเรียนนั้นตรงตามที่FooSaverสร้างa.foo_fileและb.foo_fileในสถานที่เฉพาะคุณควรทำการทดสอบโดยตรงนั่นคือทำซ้ำค่าคงที่ในการทดสอบ

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

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

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

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


4

สิ่งที่ @Erik แนะนำ - ในแง่ของการทำให้แน่ใจว่าคุณมีความชัดเจนในสิ่งที่คุณกำลังทดสอบ - ควรเป็นจุดแรกของการพิจารณา

แต่การตัดสินใจของคุณควรนำคุณไปสู่ทิศทางของการแยกตัวประกอบค่าคงที่ออกจากส่วนที่น่าสนใจของคำถามของคุณ (ถอดความ) "ทำไมฉันจึงควรปิดการทำซ้ำค่าคงที่สำหรับการทำซ้ำรหัส?" (อ้างอิงถึงตำแหน่งที่คุณพูดถึง "ทำซ้ำ [วัน] ขั้นตอน out_dir + ชื่อ")

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

os.path.join(self.out_dir, name)

ในทางกลับกันฉันจะแนะนำสิ่งนี้ ที่นี่การเน้นแสดงให้เห็นว่าคุณมีเส้นทางและคุณ "เสียบ" ชื่อไฟล์ใบไม้:

self.assertTrue(os.path.isfile("/tmp/special_name/{0}".format(FooSaver.A_FILENAME)))

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


1

ฉันเห็นด้วยกับคำตอบของ Erik Eidt แต่มีตัวเลือกที่สาม: เริ่มต้นค่าคงที่ในการทดสอบดังนั้นการทดสอบจะผ่านแม้ว่าคุณจะเปลี่ยนค่าของค่าคงที่ในรหัสการผลิต

(ดูการถูค่าคงที่ใน python unittest )

foo = FooSaver("/tmp/special_name")
foo.save_type_a()
foo.save_type_b()

with mock.patch.object(FooSaver, 'A_FILENAME', 'unique_to_your_test_a'):
  self.assertTrue(os.path.isfile("/tmp/special_name/unique_to_your_test_a"))
with mock.patch.object(FooSaver, 'B_FILENAME', 'unique_to_your_test_b'):
  self.assertTrue(os.path.isfile("/tmp/special_name/unique_to_your_test_b"))

และเมื่อทำสิ่งต่าง ๆ เช่นนี้ฉันมักจะทำการทดสอบสติที่ฉันเรียกใช้การทดสอบโดยไม่มีwithคำสั่งและให้แน่ใจว่าฉันเห็น "'a.foo_file'! = 'unique_to_your_test_a'" แล้วใส่withคำสั่งกลับมาในการทดสอบ ดังนั้นมันจึงผ่านไปอีกครั้ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.