Testler ve üretim kodu arasında sabitler kopyalanıyor mu?


20

Testler ve gerçek kod arasında veri kopyalamak iyi mi kötü mü? Örneğin, FooSaverbelirli bir dizine sahip dosyaları belirli bir dizine kaydeden bir Python sınıfım olduğunu varsayalım :

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")

Şimdi benim testte tüm bu dosyaların oluşturulduğundan emin olmak istiyorum, bu yüzden böyle bir şey söylemek istiyorum:

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"))

Bu, dosya adlarını iki yerde çoğaltmasına rağmen, bence iyi: diğer ucundan çıkmayı beklediğim şeyi tam olarak yazmamı zorlar, yazım hatalarına karşı bir koruma katmanı ekler ve genellikle işlerin çalıştığından emin olmamı sağlar tam beklediğim gibi. Ben değiştirirseniz biliyoruz a.foo_fileiçin type_a.foo_filegelecekte ne yapmak zorunda kalacağım bazı arama ve değiştirme benim testlerde, ama bu bir anlaşma çok büyük olduğunu sanmıyorum. Kodu ve testleri anlamamın senkronize olduğundan emin olmak için testi güncellemeyi unutursam yanlış pozitifler tercih ederim.

Bir iş arkadaşı bu çoğaltmanın kötü olduğunu düşünüyor ve her iki tarafı da böyle bir şeye yeniden yansıtmamı tavsiye ediyor:

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)

ve testte:

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

Kodun beklediğim şeyi yaptığından emin olmadığım için bunu sevmiyorum --- sadece out_dir + namehem üretim tarafında hem de test tarafında adımı kopyaladım . +Dizelerde nasıl çalıştığını anladığımda bir hatayı açığa çıkarmaz ve yazım hatalarını yakalamaz.

Öte yandan, bu dizeleri iki kez yazmaktan daha az kırılgan ve bu gibi iki dosyada veri çoğaltmak benim için biraz yanlış görünüyor.

Burada açık bir emsal var mı? Testler ve üretim kodu boyunca sabitleri çoğaltmak doğru mu yoksa çok kırılgan mı?

Yanıtlar:


16

Bence bu neyi test etmeye çalıştığınıza bağlı, bu da sınıfın sözleşmesinin ne olduğuna bağlı.

Sınıfın sözleşme tam olarak bu değilse FooSaverüretir a.foo_fileve b.foo_filebelirli bir konumdaki, o zaman, yani doğrudan o sınamak testlerde sabitleri çoğaltmak gerekir.

Bununla birlikte, sınıfın sözleşmesi, geçici bir alana iki dosya oluşturmasıysa, her birinin adı özellikle çalışma zamanında kolayca değiştirilirse, daha genel olarak, muhtemelen testlerin dışında kalan sabitleri kullanarak daha genel olarak test etmeniz gerekir.

Bu yüzden iş arkadaşınızla daha üst düzey bir alan tasarımı perspektifinden sınıfın gerçek doğası ve sözleşmesi hakkında tartışmalısınız. Eğer kabul edemezseniz, bunun test etmek yerine sınıfın kendisinin anlama ve soyutlama seviyesi ile ilgili bir konu olduğunu söyleyebilirim.

Ayrıca sınıfın yeniden düzenleme sırasındaki sözleşmesinin değiştiğini, örneğin soyutlama seviyesinin zaman içinde yükselmesi için de mantıklıdır. İlk başta, belirli bir geçici konumda iki özel dosya hakkında olmakla birlikte, zamanla ek soyutlamanın garanti edilebileceğini görebilirsiniz. Böyle bir durumda, testleri sınıf sözleşmesiyle senkronize olacak şekilde değiştirin. Sadece test ettiğiniz için sınıfın sözleşmesini derhal inşa etmeye gerek yoktur (YAGNI).

Bir sınıfın sözleşmesi iyi tanımlanmadığında, onu test etmek, sınıfın doğasını sorgulamamıza neden olabilir, ancak onu kullanacaktır. Sadece test ettiğiniz için sınıf sözleşmesini yükseltmemeniz gerektiğini söyleyebilirim; sınıfın sözleşmesini, alan adı için zayıf bir soyutlama gibi başka nedenlerle yükseltmelisiniz ve eğer değilse, olduğu gibi test edin.


4

@Erik'in önerdiği - test ettiğiniz şey konusunda net olmanızı sağlamak için - kesinlikle ilk bakış açınız olmalıdır.

Ancak kararınız sizi sabitin çarpanlarına ayırma yönüne götürmeli, bu da sorunuzun ilginç bir kısmını bırakıyor (yorumlama) "Neden yinelenen sabitleri kod çoğaltmak için takas etmeliyim?". ("Out_dir + name adımı" hakkında yinelenen konu hakkında referans olarak.)

Ben en çok durumları ki (Erik'in yorumlarınızı modulo) iman yapmak yinelenen sabitleri kaldırmasını yarar. Ama gelmez bir şekilde yapmanız gerekir değil yinelenen kod. Özel örneğinizde bu kolaydır. Üretim kodunuzda "ham" dizeler olarak bir yolla uğraşmak yerine, yolu yol olarak kabul edin. Bu, yol bileşenlerini birleştirmenin dize birleştirmesinden daha sağlam bir yoludur:

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

Test kodunuzda ise böyle bir şey öneririm. Burada vurgu, bir yolunuz olduğunu ve bir yaprak dosyası adını "eklediğiniz" anlamına gelir:

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

Yani, dil öğelerinin daha düşünceli bir şekilde seçilmesiyle kod çoğaltmasını otomatik olarak önleyebilirsiniz. (Her zaman değil, sık sık deneyimlerime göre.)


1

Erik Eidt'in cevabına katılıyorum, ancak üçüncü bir seçenek var: testteki sabiti saptırın, böylece üretim kodundaki sabitin değerini değiştirseniz bile test geçer.

( python unittest'te bir sabiti saplama konusuna bakın )

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"))

Ve böyle şeyler yaparken, genellikle withifadeleri olmadan testleri yaptığım bir akıl sağlığı testi yaptığınızdan ve "'a.foo_file'! = 'Unique_to_your_test_a'" ifadesini gördüğümden emin olurum, sonra withifadeyi tekrar teste koyarım böylece tekrar geçer.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.