31 sierpnia 2014

[python] mock_open - testowanie uchwytu do pliku

Coś czego brakowało mi podczas pisania skryptów, czyli metody na zmockowanie bibliotecznej funkcji open (do otwierania plików). Framework do mocków posiada specjalną metodę mock.mock_open(), jednak wszystkie przykłady jakie znalazłem na sieci pokazywały działanie tego mechanizmu w interpreterze, co jakoś nie współgrało z moimi TestSuite-ami. Wydaje mi się, że da się to rozwiązać w bardziej elegancji sposób, ale póki co jest to coś co działa.

Przykład: zapisywanie i odczytywanie z pliku:
#! /usr/bin/env python3
import os

def fileWrite(fileName):
    f = open(fileName, 'w+')
    f.write('text\n')
    f.close()

def fileRead(fileName):
    f = open(fileName, 'r')
    count = 0
    for _ in f:
        count += 1
    f.close()
    return count
Po pierwsze nie udało mi się skorzystać z patch za pomocą dekoratora, a jedynie przez with. Także wywołanie jest troszkę inne, jako drugi parametr, przekazywany jest mock, stworzony przez mock.mock_open() (zamiast domyślnie wygenerowanego z MagicMock). Parametr create odpowiada za wygenerowanie atrybutów, jego brak sprawia, że zgłaszany jest brak atrybutu asdf.open() przez test. Tego jeszcze nie ogarniam.
W test_filterWrite() zawołanie mocka m, wygeneruje handler - czyli zadziała jak open(), które zwiększy licznik wywołań, dlatego można z tego skorzystać, dopiero po sprawdzeniu asercji (linijka 14).

Jeżeli potrzebujemy aby z uchwytu można było pobrać dane (przykład test_fileRead), istnieje alternatywne rozwiązanie z przesłonięciem __enter__ oraz __iter__. Nie zwiększa to licznika open i assert w linijce 27, będzie działał poprawnie.
import unittest
from unittest import mock
from unittest.mock import mock_open
import asdf

class TestFindFile(unittest.TestCase):
    def test_fileWrite(self):
        m = mock_open()
        with mock.patch('asdf.open', m, create=True):
            fileName = 'aaa.txt'
            asdf.fileWrite(fileName)

            m.assert_called_once_with(fileName, 'w+')
            file_handler = m()
            file_handler.write.assert_called_once_with('text\n')
            file_handler.close.assert_called_once_with()

    def test_fileRead(self):
        m = mock_open()
        with mock.patch('asdf.open', m, create=True):
            fileName = 'aaa.txt'
            file_handler = m.return_value.__enter__.return_value
            file_handler.__iter__.return_value = ['line1', 'line2']

            self.assertEqual(2, asdf.fileRead(fileName))

            m.assert_called_once_with(fileName, 'r')
            file_handler.close.assert_called_once_with()

Brak komentarzy:

Prześlij komentarz