1 września 2014

[python] HTTP POST requests

Zadanie: jak wykonać zapytanie POST do strony. Dla testów skorzystałem z fajnej stronki http://requestb.in/, bo nie chciało mi się stawiać własnego serwera. Trzeba sobie za jej pomocą wygenerować link, który będzie służył nam do testów.
Najpierw standardowa biblioteka urllib2. W python-ie 3, została podzielona na dwie, urllib.request oraz urllib.error. W pierwszej kolejności przerabiamy parametry, które wstawimy do zapytania za pomocą urlencode() do "url-encoded" string (spacja na %20 itp.). Następnie tworzymy zapytanie url - request.Request(). Jeżeli zostanie dostarczony parametr data (jak w tym przypadku), to zapytanie będzie typu POST.
#! /usr/bin/env python3

import urllib.parse
import urllib.request

def post1():
    url = 'http://requestb.in/kcco1337'
    post_data = urllib.parse.urlencode({'login': 'user1', 'password': 'hasło 1'})

    binary_data = post_data.encode('utf-8')
    req = urllib.request.Request(url, data=binary_data)
    u = urllib.request.urlopen(req)
    print(u.read())
Raw body odebrane przez requestb.in:
login=user1&password=has%C5%82o+1
Urllib zbiera sporo krytyki, jako nieodpowiadający naszym czasom i trudny w obsłudze (muszę się z tym troszkę zgodzić). Alternatywę, którą znalazłem jest biblioteka requests, której działania nie muszę nawet opisywać.
#! /usr/bin/env python3

import requests

def post2():
    url = 'http://requestb.in/kcco1337'
    r = requests.post(url, data={'login': 'user1', 'password': 'hasło 2'})
    print(r.status_code)
    print(r.content)
Jedyną rzeczą, której nie jestem pewny jest kodowanie, najwyraźniej jednak w tym przykładzie wszystko poszło ok. Odebrane raw body:
password=has%C5%82o+2&login=user1

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

29 sierpnia 2014

python "with"

Chociaż w samym języku "with" jest już od dawna, dopiero teraz się z nim mierze. with umożliwia bezpieczne zarządzanie obiektem (po opuszczeniu bloku, zwalnia zasoby, które przy wejściu były zarezerwowane). W klasycznym przykładzie z open(), with otwiera plik i zwraca uchwyt do niego (przez as), który zostanie zwolniony po opuszczeniu bloku (automatycznie zostanie zawołane f.close()).
with open("aaa.txt") as f:
    data = f.read()
Mechanizm ten można stosować, gdy obiekt, który przekażemy do with posiada metody __enter__() (która będzie wołana na wejściu do bloku, oraz metodę __exit__() (która zostanie zwołana przy wyjściu z bolku). Jeżeli podczas wykonywania zostanie rzucony jakiś wyjątek, w pierwszej kolejności zostanie zwolniony zasób, a następnie wyjątek zostanie wyrzucony wyżej. Tutaj dwa linki, które dokładniej wyjaśniają wszystkie zawiłości.

28 sierpnia 2014

[C++] RAII - Resource Acquisition Is Initialization

Notatka z wykładu Michaela Caissea "Introduction to Modern C++ Techniques" na C++Now (dawniej BoostCon), dotycząca wzorca projektowego RAII. Choć mam okazję korzystać z jej dobrodziejstw przy okazji boost-a, oraz biblioteki standardowej to jednak sam nigdy nic nie napisałem. Technika polega na przyjęciu zasobu, podczas konstruowania obiektu i zwalniania podczas jego destrukcji. Programista nie musi pamiętać o wykonaniu dodatkowych czynności, gdy zasób już nie będzie potrzebny, zwalnianiem zajmie się kompilator.
Przykład poniżej pokazuje zarządzeniem uchwytem do pliku (w konstruktorze plik jest otwierany, w destruktorze niszczony), za pomocą funkcji znanych z C.
#include <iostream>
#include <string>
#include <cstdio>

class FileManager {
public:
    FileManager(const std::string& filePath) {
        std::cout << "Open file" << std::endl;
        _fileHandler = std::fopen(filePath.c_str(), "w+");
    }

    void write(const std::string& str) {
        if(_fileHandler == nullptr)
            return;

        if(std::fputs(str.c_str(), _fileHandler) == EOF)
            throw std::exception();
    }

    ~FileManager() noexcept {
        std::cout << "Close file" << std::endl;
        if(_fileHandler != nullptr)
            std::fclose(_fileHandler);
    }

private:
    FILE* _fileHandler;
};

void modifyFile() {
    FileManager f("aaa.txt");
    f.write("some text\n");
}

int main() {
    modifyFile();
    std::cout << "End" << std::endl;
    return 0;
}
Wynik:
Open file
Close file
End

27 sierpnia 2014

[python] mockowanie iterowanych obiektów pętli

Problem, który wciąż do mnie powracał. Jak zmockować funkcję (generator), wołaną z pętli for. Ponieważ taka funkcje może zwrócić kilka wyników, cały czas próbowałem zaprząc do działania "side_effect" - bezskutecznie. Myślałem, że jej działanie jest podobne do generatora, cóż znów się czegoś nauczyłem. Pętla wymaga aby na obiekcie (z którego pobierane są dane) dało się iterować, dlatego "return_value" zwracające np. listę rozwiązuje problem. "side_effect" nadaje się bardziej do mockowania zachowań wewnątrz pętli.

Przykład z os.walk(), która zwraca trzy elementową krotkę:
import os

def findFile(fileName):
    for root, dirs, files in os.walk('~/'):
        if fileName in files:
            return os.path.join(root, fileName)

    return None
Test:
import unittest
import mock
from mock import call
import asdf

class TestFindFile(unittest.TestCase):
    @mock.patch('asdf.os')
    def test_findFIle(self, mockOs):
        fileName = 'aaa.txt'

        mockOs.walk.return_value = [('/home/user',        ('folder',),      ('bbb.txt',)),
                                    ('/home/user/folder', ('fol1', 'fol2'), (fileName,))]
        mockOs.path.join.return_value = '/home/user/folder' + fileName

        self.assertEqual('/home/user/folder' + fileName, asdf.findFile(fileName))

        mockOs.walk.assert_called_once_with('~/')
        mockOs.path.join.assert_called_once_with('/home/user/folder', fileName)

17 sierpnia 2014