Özel bir Django manage.py komutunu doğrudan bir test sürücüsünden nasıl çağırabilirim?


174

Veritabanı tablosunda bir arka uç işlemi yapan bir Django manage.py komutu için birim sınaması yazmak istiyorum. Yönetim komutunu doğrudan koddan nasıl çağırabilirim?

Test.py'den İşletim Sistemi kabuğunda komutu yürütmek istemiyorum çünkü manage.py test (test veritabanı, test kukla e-posta giden kutusu, vb.) Kullanarak ayarlanan test ortamını kullanamıyorum

Yanıtlar:


312

Bu tür şeyleri test etmenin en iyi yolu - gerekli işlevselliği komutun kendisinden bağımsız işleve veya sınıfa çıkarın. "Komut yürütme" lerden soyutlanmaya ve ek gereksinimler olmadan test yazmaya yardımcı olur.

Ancak, herhangi bir nedenle mantık formu komutunu çözemiyorsanız, call_command yöntemini kullanarak herhangi bir koddan çağırabilirsiniz :

from django.core.management import call_command

call_command('my_command', 'foo', bar='baz')

19
Test edilebilir mantığı başka bir yere koymak için +1 (model yöntemi? Yönetici yöntemi? Bağımsız işlev?) Böylece call_command makineleriyle uğraşmanıza gerek kalmaz. Ayrıca işlevselliğin yeniden kullanımını kolaylaştırır.
Carl Meyer

36
Mantık ayıklasanız bile bu işlev, gerekli bağımsız değişkenler gibi komuta özgü davranışınızı test etmek ve kitaplık işlevinizi çağırdığından emin olmak için hala yararlıdır cadı gerçek işi yapar.
Igor Sobreira

Açılış paragrafı herhangi bir sınır durumu için geçerlidir . Kendi biz mantık kodunuzu, kullanıcı gibi bir şeyle arayüzle sınırlı kodun dışına taşıyın. Ancak, kod satırını yazarsanız, bir hata olabilir, bu nedenle testler gerçekten herhangi bir sınırın arkasına ulaşmalıdır.
Phlip

call_command('check')Sistem kontrollerinin bir testte geçtiğinden emin olmak için bunun hala yararlı olduğunu düşünüyorum .
Adam Barnes

22

Call_command hile yapmak yerine, görevinizi şu şekilde çalıştırabilirsiniz:

from myapp.management.commands import my_management_task
cmd = my_management_task.Command()
opts = {} # kwargs for your command -- lets you override stuff for testing...
cmd.handle_noargs(**opts)

10
Neden call_command stdin, stdout, stderr'i yakalamayı sağlar? Belgeler bunu yapmanın doğru yolunu ne zaman belirtir?
boatcoder

17
Bu çok iyi bir soru. Üç yıl önce belki sizin için bir cevap verebilirdim;)
Nate

1
Ditto Nate - cevabı bir buçuk yıl önce bulduğum şey olduğunda - sadece üzerine inşa ettim ...
Danny Staple

2
Kazma sonrası, ama bugün bu bana yardımcı oldu: Ben her zaman benim kod tabanı (kullanılan Django sitesine bağlı olarak) tüm uygulamaları kullanmıyorum ve call_commandyüklenecek test uygulaması gerekiyor INSTALLED_APPS. Sadece test amacıyla uygulamayı yüklemek zorunda ve bunu kullanarak arasında, ben seçti.
Mickaël

call_commandmuhtemelen çoğu insan ilk önce denemelidir. Bu yanıt, unicode tablo adlarını inspectdbkomuta iletmem gereken bir soruna geçici olarak yardımcı oldu . python / bash komut satırı argümanlarını ascii olarak yorumluyordu ve bu da django'daki get_table_descriptionçağrıyı bombaladı .
bigh_29

17

aşağıdaki kod:

from django.core.management import call_command
call_command('collectstatic', verbosity=3, interactive=False)
call_command('migrate', 'myapp', verbosity=3, interactive=False)

... terminalde yazılan aşağıdaki komutlara eşittir:

$ ./manage.py collectstatic --noinput -v 3
$ ./manage.py migrate myapp --noinput -v 3

Bkz . Django dokümanlarından yönetim komutlarını çalıştırma .


14

Call_command üzerinde Django dokümantasyon söz başarısız outyönlendirilir olmalıdır sys.stdout. Örnek kod şöyle olmalıdır:

from django.core.management import call_command
from django.test import TestCase
from django.utils.six import StringIO
import sys

class ClosepollTest(TestCase):
    def test_command_output(self):
        out = StringIO()
        sys.stdout = out
        call_command('closepoll', stdout=out)
        self.assertIn('Expected output', out.getvalue())

1

Nate'in cevabına dayanarak bu var:

def make_test_wrapper_for(command_module):
    def _run_cmd_with(*args):
        """Run the possibly_add_alert command with the supplied arguments"""
        cmd = command_module.Command()
        (opts, args) = OptionParser(option_list=cmd.option_list).parse_args(list(args))
        cmd.handle(*args, **vars(opts))
    return _run_cmd_with

Kullanımı:

from myapp.management import mycommand
cmd_runner = make_test_wrapper_for(mycommand)
cmd_runner("foo", "bar")

Buradaki avantaj, ek seçenekler ve OptParse kullandıysanız, bu durumun sizin için çözülmesidir. Oldukça mükemmel değil - ve henüz çıktıları birleştirmiyor - ancak test veritabanını kullanacak. Daha sonra veritabanı efektlerini test edebilirsiniz.

Micheal Foords sahte modülünün kullanıldığından eminim ve ayrıca test süresince stdout'u yeniden kablolamak, bu teknikten biraz daha fazla yararlanabileceğiniz anlamına gelir - çıktıyı, çıkış koşullarını vb.


4
Neden sadece call_command kullanmak yerine tüm bu sorunlara gidesin ki?
boatcoder
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.