Python Unit Tests

Dave Angel davea at davea.name
Sat Sep 28 02:11:59 EDT 2013


On 28/9/2013 00:52, melwin9 at gmail.com wrote:

> Hey,
>

What version of Python are you using?  I'll assume 2.7, but you really
should specify it (and any other meaningful environment dependencies) in
your original query, the beginning of your thread.

For 2.7, the docs for unittest are at:

http://docs.python.org/2/library/unittest.html

> How do i go about coming up/coding tests for this program below. Not sure how to even approach writing about 5 tests for it.
>
> Initially I had this for the test but its not working well.
>
> Test was name test_guess.py (Code Below)
>
> [code]
> from unittest import TestCase 
> import pexpect as pe 
>
> import guess as g 
> import random 
>
> random_number = random.randrange(1, 10) 
> correct = False 
>
> class GuessTest(TestCase): 
>     def setUp(self): 
>         self.intro = 'I have chosen a number from 1-10' 
>         self.request = 'Guess a number: ' 
>         self.responseHigh = "That's too high." 
>         self.responseLow  = "That's too low." 
>         self.responseCorrect = "That's right!" 
>         self.goodbye = 'Goodbye and thanks for playing!' 

These strings don't change from one test to another.  Thus they could be
set in the __init__() method, and indeed could just be globals.  Or even
(horrors) imported from the guess module.  So in your tests, you might
refer to  g.intro, rather than self.intro   Or you might decide that the
content of the strings is not what you're testing, but rather the flow
of the logic.


>         
>     def test_main(self): 
>         #cannot execute main now because it will 
>         #require user input 
>         from guess import main 

No need for an import here.  Assuming you will be adding a call to
main(), you can just call it as  g.main(), since you imported g already
as a global.

>         
>     def test_guessing_hi_low_4(self): 
>         # Conversation assuming number is 4 
>         child = pe.spawn('python guess.py') 
>         child.expect(self.intro,timeout=5) 
>         child.expect(self.request,timeout=5) 
>         child.sendline('5') 
>         child.expect(self.responseHigh,timeout=5) 
>         child.sendline('3') 
>         child.expect(self.responseLow,timeout=5) 
>         child.sendline('4') 
>         child.expect(self.responseCorrect,timeout=5) 
>         child.expect(self.goodbye,timeout=5) 
>
>
>     def __init__(self): 
>         self.number = random.randint(0,10) 
>         HIGH = 1 
>         LOW = 2 
>         OK = 3 

Those 3 statements do nothing useful.  They set 3 local variables which
are then immediately forgotten.  Presumably you meant:

       self.HIGH = 1
       self.LOW = 2
       self.OK = 3

>
>     def guess(self, number): 
>         if number > self.number: 
>          return self.HIGH 
>         if number < self.number: 
>          return self.LOW 
>         return self.OK 
>
>     def test_guesstoolow(self): 

Nothing in this method tests any of the actual program

>         while not correct: 
>             guess = input("What could it be?") 
>             if guess == random_number: 
>                 print "Congrats You Got It" 
>                 correct = True 
>             elif guess > random_number: 
>                 print "To High" 
>             elif guess < random_number: 
>                 print "To Low" 
>             else: 
>                 print "Try Again"  [/code]
>
> Python code for game below
>
> [code]
> import random
>
> intro = 'I have chosen a number from 1-10'
> request = 'Guess a number: '
> responseHigh = "That's too high."
> responseLow  = "That's too low."
> responseCorrect = "That's right!"
> goodbye = 'Goodbye and thanks for playing!'
>
> print(intro)
>
> def main():
>     guessesTaken = 0
>     number = random.randint(1, 10)
>     while guessesTaken < 5:
>         print(request)
>         guess = input()
>         guess = int(guess)
>
>         guessesTaken = guessesTaken + 1
>
>         if guess < number:
>             print(responseLow) 
>
>         if guess > number:
>             print(responseHigh)
>
>         if guess == number:
>             break
>
>     if guess == number:
>             guessesTaken = str(guessesTaken)
>             print(responseCorrect + '! You guessed my number in ' + guessesTaken + ' guesses!')
>
>     if guess != number:
>         number = str(number)
>         print(goodbye + ' The number I was thinking of was ' + number)
>
> ##def main():
> #    print(intro)
>  #   user_input = raw_input(request)
>   #  print(responseHigh)
>   #  print(request)
>   #  user_input = raw_input(request)
>   #  print(responseLow)
>   #  user_input = raw_input(request)
>   #  print(responseCorrect)
>   #  print(goodbye)
>
> if __name__ == '__main__':
>     main()[/code]

According to https://en.wikipedia.org/wiki/Unit_testing:
"...one can view a unit as the smallest testable part of an application"

This is frequently a function, but certainly nothing smaller.  So  your
program is too monolithic to write multiple unit tests for.  You need to
refactor your program to make it more testable.

1) have a way to eliminate the calls to random.randint().
2) separate out the calculation from the input and output
    There has to be a better way to do this than pexpect.
    One way (probably still not the best) is to replace the print
statement and the input calls with function calls to something that the
test overrides.

Now the question of what could you possibly do in 6 tests.

Once the code has been refactored into several functions, one of those
functions probably picks the original random number.  So you can test
that function by repeatedly calling it and making sure that every
return value is a number within the specified range.

A second test might be to make sure that you get exactly 5 wrong
guesses.

A third test might be to make sure that several pre-selected sequences
of guesses elicit the right responses.  So the test might be driven from
a tuple consisting of the number, then a series of tuples where each of
the subtuples is a guess and an expected response.

A fourth test might be to stuff invalid data into the simulated input,
and make sure the program behaves as expected.  Probably throwing an
exception like it does now isn't the expected behavior.

I've run out of ideas.  But several of these could be considered
multiple tests anyway.

-- 
DaveA





More information about the Python-list mailing list