Pytest for ex49

Hello.

After a long time I have put my self together and finished ex49.
I have uploaded to Github.
I also put it in here to read and comment.
At last I managed to figure out how to make simple fixtures (there is a lot of advanced examples if you Google).
I think this test cover most of parser.py.
[edit]
I have found out how to write the tests for the error messages.
See update below.

Please feel free to make comments or suggestion on how to improve.
I also hope that it can be useful for for other who want to use Pytest.


from ex48 import parser, lexicon
from ex48.parser import *
import pytest

@pytest.fixture
def wordlist_fix():
    return lexicon.scan('the princess kill bear')

# Not needed anymore.
# @pytest.fixture
# def wordlist_error_fix():
#    return lexicon.scan('the bear princess kill') # Sentence with Yoda 'grammar' 

# This fixture replaces fixture above:
@pytest.fixture
def parse_error_msg():
    return lexicon.scan('the the the the') # All the "the" is for NOT match verb, noun or direction


def test_Sentence():
    a = Sentence(('noun','princess'), ('verb', 'kill'), ('noun', 'bear'))
    assert a.subject == 'princess'
    assert a.verb == 'kill'
    assert a.object == 'bear'

def test_peek(wordlist_fix):
    assert parser.peek(wordlist_fix) ==  'stop'  

def test_peek_empty():
    wordlist = []
    assert parser.peek(wordlist) == None

def test_match(wordlist_fix):
    w = wordlist_fix.pop(0)
    assert w == ('stop','the') # Popped off from wordlist_fix
    x = wordlist_fix
    assert x == [('noun', 'princess'),('verb', 'kill'), ('noun', 'bear')]
    # Remainder of wordlist_fix

def test_skip(wordlist_fix):
    assert peek(wordlist_fix) == 'stop'

def test_parse_verb(wordlist_fix):
    skip(wordlist_fix, 'stop')
    match(wordlist_fix, 'expecting') 
    # "expecting" can be replaced with anything to make peek(wordlist_fix) equal to 'verb'
    # otherwise it only equals 'noun'.  (correct ?) 
    assert peek(wordlist_fix) == 'verb'

def test_object(wordlist_fix):
    skip(wordlist_fix, 'stop')
    next_word = peek(wordlist_fix)
    assert next_word == 'noun'

def test_subject(wordlist_fix):
    skip(wordlist_fix, 'stop')
    next_word = peek(wordlist_fix)
    assert next_word == 'noun'
    skip(wordlist_fix, 'verb')
    next_word = peek(wordlist_fix)
    assert next_word == 'noun'

def test_parse_sentence(wordlist_fix):
    subj = parse_subject(wordlist_fix)
    verb = parse_verb(wordlist_fix)
    obj = parse_object(wordlist_fix)
    assert subj == ('noun', 'princess')
    assert verb == ('verb', 'kill')
    assert obj == ('noun', 'bear')

def test_error_message():
    with pytest.raises(ParserError) as excinfo:
         parse_sentence([('noun', 'princess'),('bear', 'kill'), ('a', 'error')])
    assert str(excinfo.value) == 'Expected a verb next.'

def test_error_message2(wordlist_error_fix):
    with pytest.raises(ParserError) as excinfo:
        parse_sentence(wordlist_error_fix)
assert str(excinfo.value) == 'Expected a verb next.'

Did some work on the error messages.
I have changed the last test functions in previous code (above) with the three following functions (below).

def test_error_message_parse_verb(parse_error_msg):
    with pytest.raises(ParserError) as excinfo:
        parse_verb(parse_error_msg)
    assert str(excinfo.value) == 'Expected a verb next.'

def test_error_message_parse_subject(parse_error_msg):
    with pytest.raises(ParserError) as excinfo:
        parse_subject(parse_error_msg)
    assert str(excinfo.value) == 'Expected a verb next.'

def test_error_message_parse_object(parse_error_msg):
    with pytest.raises(ParserError) as excinfo:
        parse_object(parse_error_msg)
    assert str(excinfo.value) == 'Expected a noun or direction next.'

The fixture: wordlist_error_fix is not needed anymore.

Opps. Almost forgot.
This fixture is needed instead:

@pytest.fixture
def parse_error_msg():
    return lexicon.scan('the the the the') 
# All the "the" is for NOT match verb, noun or direction
2 Likes

Hey, that actually looks pretty good. I’m happy with it.

@ulfen69 Thanks for posting this.
It’s super helpful to see how you use pytest to test this exercise…
When using fixtures are they connected to the function by just putting it on the line above the function?

You mean the line with the @? Yes, that’s a decorator, a very handy construct in Python. It modifies the function or class below.

Hi @ktrager

It was a while I did this one. I hope I still remember it correct.
The first one “declare” the fixture

@pytest.fixture

The rest is the “setup”

@pytest.fixture
def parse_error_msg():
    return lexicon.scan('the the the the') 
# All the "the" is for NOT match verb, noun or direction
 

Now you can use the same output in many different tests
just put the name between brackets.

def test_something(parse_error_msg):
    assert  ... ==  ...

def test_something_else(parse_error_msg):
    assert  *** == ***