Kode pengujian unit Python yang memanggil fungsi python tingkat OS/Modul

Saya memiliki modul/skrip python yang melakukan beberapa di antaranya

  1. Di berbagai level bersarang di dalam skrip saya mengambil input baris perintah, memvalidasinya, menerapkan default yang masuk akal
  2. Saya juga memeriksa apakah ada beberapa direktori

Hal di atas hanyalah dua contoh. Saya mencoba mencari "strategi" apa yang terbaik untuk menguji ini. Apa yang telah saya lakukan adalah membuat fungsi pembungkus di sekitar raw_input dan os.path.exists dalam modul saya dan kemudian dalam pengujian saya, saya mengganti kedua fungsi ini untuk mengambil masukan dari daftar array saya atau melakukan beberapa perilaku diolok-olok. Pendekatan ini memiliki kelemahan sebagai berikut

  1. Fungsi pembungkus hanya ada demi pengujian dan ini mencemari kode
  2. Saya harus ingat untuk menggunakan fungsi wrapper dalam kode setiap saat dan tidak hanya memanggil os.path.exists atau raw_input

Ada saran cemerlang?


person Kannan Ekanath    schedule 19.02.2013    source sumber
comment
Saya menambahkan beberapa tag lagi sehingga pertanyaan ini mendapat lebih banyak perhatian.   -  person User    schedule 19.02.2013


Jawaban (3)


Jawaban singkatnya adalah dengan monkey patch panggilan sistem ini.

Ada beberapa contoh bagus dalam jawaban Cara menampilkan stdin yang dialihkan di Piton?

Berikut adalah contoh sederhana untuk raw_input() menggunakan lambda yang membuang prompt dan mengembalikan apa yang kita inginkan.

Sistem Sedang Diuji

$ cat ./name_getter.py
#!/usr/bin/env python

class NameGetter(object):

    def get_name(self):
        self.name = raw_input('What is your name? ')

    def greet(self):
        print 'Hello, ', self.name, '!'

    def run(self):
        self.get_name()
        self.greet()

if __name__ == '__main__':
    ng = NameGetter()
    ng.run()

$ echo Derek | ./name_getter.py 
What is your name? Hello,  Derek !

Kasus cobaan:

$ cat ./t_name_getter.py
#!/usr/bin/env python

import unittest
import name_getter

class TestNameGetter(unittest.TestCase):

    def test_get_alice(self):
        name_getter.raw_input = lambda _: 'Alice'
        ng = name_getter.NameGetter()
        ng.get_name()
        self.assertEquals(ng.name, 'Alice')

    def test_get_bob(self):
        name_getter.raw_input = lambda _: 'Bob'
        ng = name_getter.NameGetter()
        ng.get_name()
        self.assertEquals(ng.name, 'Bob')

if __name__ == '__main__':
    unittest.main()

$ ./t_name_getter.py -v
test_get_alice (__main__.TestNameGetter) ... ok
test_get_bob (__main__.TestNameGetter) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
person Johnsyweb    schedule 19.02.2013

Solusi 1: Saya akan melakukan hal seperti ini karena berhasil:

def setUp(self):
    self._os_path_exists = os.path.exists
    os.path.exists = self.myTestExists # mock

def tearDown(self):
    os.path.exists = self._os_path_exists

Hal ini tidak begitu baik.

Solusi 2: Merestrukturisasi kode Anda bukanlah suatu pilihan seperti yang Anda katakan, bukan? Ini akan membuat pemahaman menjadi lebih buruk dan tidak intuitif.

person User    schedule 19.02.2013

Johnnysweb sangat tepat dengan apa yang perlu Anda lakukan, namun alih-alih membuat sendiri, Anda dapat mengimpor dan menggunakan mengolok-olok. Mock dirancang khusus untuk pengujian unit, dan membuatnya sangat mudah untuk melakukan apa yang Anda coba lakukan. Ini bawaan untuk Python 3.3.

Misalnya, jika ingin menjalankan pengujian unit yang menggantikan os.path.isfile dan selalu mengembalikan True:

try:
    from unittest.mock import patch
except ImportError:
    from mock import patch

class SomeTest(TestCase):

    def test_blah():
        with patch("os.path.isfile", lambda x: True):
            self.assertTrue(some_function("input"))

Ini dapat menghemat banyak kode boilerplate, dan cukup mudah dibaca.

Jika Anda memerlukan sesuatu yang lebih rumit, misalnya, mengganti supbroccess.check_output, Anda dapat membuat fungsi pembantu sederhana:

def _my_monkeypatch_function(li):
     x,y = li[0], li[1]
     if x == "Reavers":
        return "Gorram"
     if x == "Inora":
        return "Shiny!"
     if x == y:
        return "The Ballad of Jayne"

def test_monkey():
     with patch("subprocess.check_output", _my_monkeypatch_function):
            assertEquals(subprocess.check_output(["Mudder","Mudder"]),
                                                 "The Ballad of Jayne")
person Community    schedule 30.12.2013