2013-08-10 14 views
79

Mam moduł Python, który używa biblioteki argparse. Jak napisać testy dla tej części bazy kodu?Jak pisać testy dla części argarse modułu python?

+6

Nie jestem pewien, jak to powiedzieć bez natknięcia się na szarpnięcia, ale w jaki sposób pomaga mi połączenie z czymś, nawet bez słowa "test"? – pydanny

+0

argparse to interfejs linii poleceń. Napisz testy, aby wywołać aplikację za pomocą wiersza poleceń. – Homer6

Odpowiedz

100

Należy byłaby kod i przenieść parsowania do funkcji:

def parse_args(args): 
    parser = argparse.ArgumentParser(...) 
    parser.add_argument... 
    # ...Create your parser as you like... 
    return parser.parse_args(args) 

Następnie w funkcji main należy właśnie nazywamy go:

parser = parse_args(sys.argv[1:]) 

(gdzie pierwszy element z sys.argv, który reprezentuje nazwę skryptu, został usunięty, aby nie wysyłać go jako dodatkowego przełącznika podczas działania interfejsu CLI.)

W swoich badaniach, można następnie wywołać funkcję parsera ze cokolwiek lista argumentów chcesz przetestować go z:

def test_parser(self): 
    parser = parse_args(['-l', '-m']) 
    self.assertTrue(parser.long) 
    # ...and so on. 

W ten sposób nigdy nie będziesz musiał wykonać kod aplikacji tylko do testowania parser.

Jeśli trzeba zmienić i/lub dodać opcje do parsera później w aplikacji, a następnie utworzyć metody fabryki:

def create_parser(): 
    parser = argparse.ArgumentParser(...) 
    parser.add_argument... 
    # ...Create your parser as you like... 
    return parser 

można później manipulować, jeśli chcesz, a test mógłby wyglądać :

class ParserTest(unittest.TestCase): 
    def setUp(self): 
     self.parser = create_parser() 

    def test_something(self): 
     parsed = self.parser.parse_args(['--something', 'test']) 
     self.assertEqual(parsed.something, 'test') 
+2

Dzięki za odpowiedź. W jaki sposób testujemy błędy, gdy dany argument nie jest przekazywany? –

+2

@PratikKhadloya Jeśli argument jest wymagany i nie jest przekazywany, argparse zgłosi wyjątek. –

+0

Dobrze, ale próbuję również przetestować komunikat w wyjątku. Nie mogę uzyskać blokady komunikatu w obiekcie wyjątku. https://gist.github.com/tispratik/aebff28b8c5afd7bee59 –

4
  1. Wypełnianie listy arg za pomocą sys.argv.append() a następnie zadzwonić parse(), sprawdzić wyniki i powtórzyć.
  2. Wywołanie z pliku wsadowego/bash z flagami i flagą dump args.
  3. Połóż całą analizę argumentów w oddzielnym pliku, a następnie przetwórz i zrzuć/oceń wyniki, a następnie przetestuj to z pliku wsadowego/bash.
3

prosty sposób testowania parser:

parser = ... 
parser.add_argument('-a',type=int) 
... 
argv = '-a 1 foo'.split() # or ['-a','1','foo'] 
args = parser.parse_args(argv) 
assert(args.a == 1) 
... 

Innym sposobem jest modyfikować sys.argv i wywołać args = parser.parse_args()

Istnieje wiele przykładów testów argparse w lib/test/test_argparse.py

4

Dodać funkcja main() wziąć argv jako argument zamiast pozwolić mu read from sys.argv as it will by default:

# mymodule.py 
import argparse 
import sys 


def main(args): 
    parser = argparse.ArgumentParser() 
    parser.add_argument('-a') 
    process(**vars(parser.parse_args(args))) 
    return 0 


def process(a=None): 
    pass 

if __name__ == "__main__": 
    sys.exit(main(sys.argv[1:])) 

Następnie można przetestować normalnie.

import mock 

from mymodule import main 


@mock.patch('mymodule.process') 
def test_main(process): 
    main([]) 
    process.assert_call_once_with(a=None) 


@mock.patch('foo.process') 
def test_main_a(process): 
    main(['-a', '1']) 
    process.assert_call_once_with(a='1') 
-1

Okazało się, że najprostszym sposobem, przynajmniej dla mnie, po prostu by sprawdzić sys.argv [0] więc sprawdzić, czy pyton został prowadził jako python -m unittest i nic nie analizować, czy to był przypadek.

import sys 
import argparse 

parser = argparse.ArgumentParser() 

parser.add_argument('--outdir', help='Directory to output to', \ 
    default='out') 
parser.add_argument('--file', help='Input file', \ 
    default='section') 
parser.add_argument('--word', help='Word to look up') 

if sys.argv[0] == 'python -m unittest': 
    args = parser.parse_args([]) 
else: 
    args = parser.parse_args() 
6

„argparse część” jest nieco niejasne, więc ta odpowiedź skupia się na jednej części: metoda parse_args.Jest to metoda, która współdziała z linią poleceń i pobiera wszystkie przekazane wartości. Zasadniczo możesz wyśmiewać to, co zwraca parse_args, aby nie było potrzeby pobierania wartości z wiersza poleceń.

import argparse 
import mock 


@mock.patch('argparse.ArgumentParser.parse_args', 
      return_value=argparse.Namespace(kwarg1=value, kwarg2=value)) 
def test_command(mock_args): 
    pass 

Trzeba uwzględnić wszystkie args metoda dowodzenia w Namespace nawet jeśli nie są one przekazywane. Podaj te argumenty o wartości None. (patrz: docs) Ten styl jest przydatny do szybkiego wykonywania testów w przypadkach, w których dla każdego argumentu metody przekazywane są różne wartości. Jeśli zdecydujesz się na kpiny z samego siebie, aby całkowicie polegać na argumencie w swoich testach, upewnij się, że zachowuje się podobnie do rzeczywistej klasy Namespace.

+0

Ale teraz twój najbardziej niepoprawny kod zależy również od "argparse" i klasy "Namespace". Powinieneś kpić z 'Namespace'. –

+0

@DrunkenMaster przeprasza za złośliwy ton. Zaktualizowałem swoją odpowiedź z wyjaśnieniem i możliwymi zastosowaniami. Uczę się tutaj również, więc jeśli możesz, możesz (lub ktoś inny) dostarczyć przypadki, w których kpina z wartości zwrotu jest korzystna? (a przynajmniej przypadki, w których * nie * kpiny z wartości zwracanej są szkodliwe) –

Powiązane problemy