Haklısınız, testleriniz random
modülün işini yaptığını ; unittest yalnızca sınıfın kendisini test etmelidir, diğer kodlarla nasıl etkileşime gireceğini değil (ayrı olarak test edilmelidir).
Kodunuzun random.randint()
yanlış kullanması elbette tamamen mümkündür ; ya da random.randrange(1, self._sides)
onun yerine çağırıyorsunuz ve ölmeniz asla en yüksek değeri atmıyor, ama bu farklı bir tür hata olurdu, bir unittest ile yakalayabileceğiniz bir hata değil. Bu durumda, die
üniteniz olarak tasarlanmış çalışan, ancak tasarım kendisi hatalıydı.
Bu durumda, işlevi değiştirmek için alaycı kullanırım randint()
ve yalnızca doğru şekilde çağrıldığını doğrularım . Python 3.3 ve üstü, bu tür testleri işlemek için unittest.mock
modülle birlikte gelir , ancak aynı işlevselliği elde etmek için harici mock
paketi eski sürümlere yükleyebilirsiniz
import unittest
try:
from unittest.mock import patch
except ImportError:
# < python 3.3
from mock import patch
@patch('random.randint', return_value=3)
class TestDice(unittest.TestCase):
def _make_one(self, *args, **kw):
from die import Die
return Die(*args, **kw)
def test_standard_size(self, mocked_randint):
die = self._make_one()
result = die.roll()
mocked_randint.assert_called_with(1, 6)
self.assertEqual(result, 3)
def test_custom_size(self, mocked_randint):
die = self._make_one(sides=42)
result = die.roll()
mocked_randint.assert_called_with(1, 42)
self.assertEqual(result, 3)
if __name__ == '__main__':
unittest.main()
Alay ile, testiniz artık çok basit; gerçekten sadece 2 vaka var. 6 taraflı kalıp ve özel taraf kasa için varsayılan kasa.
randint()
İşlevinin genel ad alanındaki işlevi geçici olarak değiştirmenin başka yolları da vardır Die
, ancak mock
modül bunu en kolay hale getirir. Buradaki @mock.patch
dekoratör , test durumundaki tüm test yöntemleri için geçerlidir ; her test yöntemine ek bir argüman, alaycı random.randint()
işlev iletilir , bu yüzden gerçekten doğru çağrılıp çağrılmadığını görmek için alaycıya karşı test edebiliriz. return_value
O çağrıldığında biz olduğunu doğrulamaları, taklidinin dönen hangi argüman belirtir die.roll()
yöntem aslında bize 'rastgele' sonuç döndürdü.
Burada başka bir Python unittesting en iyi uygulamasını kullandım: testin altındaki sınıfı testin bir parçası olarak içe aktarın. Bu _make_one
yöntem, bir test içinde içe aktarma ve örnekleme işlemini gerçekleştirir , böylece orijinal modülün içe aktarılmasını engelleyecek bir sözdizimi hatası veya başka bir hata yapmış olsanız bile test modülü yüklenir.
Bu şekilde, modül kodunun kendisinde bir hata yaptıysanız, testler yine de çalıştırılacaktır; sadece başarısız olurlar, size kodunuzdaki hatayı anlatırlar.
Açık olmak gerekirse, yukarıdaki testler aşırı derecede basittir. Buradaki amaç random.randint()
, örneğin doğru argümanlarla çağrılmış olanları test etmektir. Bunun yerine amaç, belirli girdiler verildiğinde ünitenin doğru sonuçları ürettiğini test etmektir; bu girdiler, test edilmeyen diğer birimlerin sonuçlarını içerir . Alay ederekrandom.randint()
Yöntemi kodunuzdaki başka bir giriş üzerinde kontrol sahibi olursunuz.
In gerçek dünya testlerinin, üniteniz-under-testinde gerçek kod daha karmaşık olacak; API'ya iletilen girdilerle ve diğer birimlerin nasıl çağrıldığına ilişkin ilişki yine de ilginç olabilir ve alay etmek size ara sonuçlara erişmenizi sağlar ve aynı zamanda bu aramalar için dönüş değerlerini ayarlamanıza olanak tanır.
Örneğin, kullanıcıların bir 3. taraf OAuth2 hizmetine (çok aşamalı bir etkileşim) karşı kimlik doğrulaması yapan kodda, kodunuzun bu 3. taraf hizmete doğru verileri iletip iletmediğini test etmek ve bunun 3. taraf hizmet, tam bir OAuth2 sunucusu kendiniz oluşturmak zorunda kalmadan farklı senaryoları simüle etmenize izin vererek geri dönecektir. Burada, ilk yanıttan gelen bilgilerin doğru bir şekilde işlendiğini ve ikinci aşama çağrısına aktarıldığını test etmek önemlidir, bu nedenle alay edilen hizmetin doğru bir şekilde çağrıldığını görmek istersiniz.