Python Unit Tests

melwin9 at gmail.com melwin9 at gmail.com
Sun Sep 29 21:46:30 EDT 2013


Hi Terry & Dave,

Thanks for the suggestions. I am running Python 2.7 and when I tried the code above it said def main(target) <- invalid syntax. I was told to use pexpect by my professor which is why I started to use that for the tests. As for the test suggestions, I will try to come up wit those tests with my current code but again idk how to do it in pexpect which was asked of me.

On Saturday, September 28, 2013 2:47:20 PM UTC-4, Terry Reedy wrote:
> On 9/28/2013 12:52 AM, mel wrote:
> 
> [How can I test...]
> 
> 
> 
> > 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)
> 
> 
> 
> > if __name__ == '__main__':
> 
> >      main()
> 
> 
> 
> To expand on Dave's answer, I would refactor main() as below.
> 
> 
> 
> Note 1. I add 'allowed' so you can easily change the number or let the 
> 
> user decide the difficulty. One can always guess right in at most 4 tries.
> 
> 
> 
> Note 2. I am presuming that you are using 3.x.
> 
> 
> 
> allowed = 5
> 
> 
> 
> def getguess(target, allowed):
> 
>    tries = 0
> 
>    while tries < allowed:
> 
>      tries += 1
> 
>      guess = int(input(request))
> 
>      if guess < target:
> 
>        print(response_low)
> 
>      elif guess > target:
> 
>        print(response_high)
> 
>      else:
> 
>        return guess, tries
> 
> 
> 
> def main(target)
> 
>    guess, tries = getguess(target, allowed)
> 
>    if guess == number:
> 
>      print(responseCorrect + '! You guessed my number in ' + tries + ' 
> 
> guesses!')
> 
>    else:
> 
>      print(goodbye + ' The number I was thinking of was ' + number)
> 
> 
> 
> if __name__ == '__main__':
> 
>    main(random.randint(1, 10))
> 
> 
> 
> To test a function, you must be able to control inputs and access 
> 
> outputs. Unfortunately, this makes testing simple beginner programs that 
> 
> turn user input and random numbers input into screen output harder, in a 
> 
> way, than testing complicated math functions, such as one that 
> 
> approximates the derivative of a function as a point.
> 
> 
> 
> One way to control user input for getguess is to put something like the 
> 
> following (untested) in your test module. (Ignore print for the moment.)
> 
> 
> 
> class IntInput:
> 
>    "Replace input() that should return int as string."
> 
>    def __init__(self, ints, print=None)
> 
>      "Ints must be a sequence of ints"
> 
>      self.i = -1  # so 0 after first increment
> 
>      self.ints = ints
> 
>      self.print = print
> 
>    def input(prompt):
> 
>      "Maybe save prompt, return str(int)."
> 
>      if self.print:
> 
>        self.print(prompt)
> 
>      i = self.i + 1
> 
>      self.i = i
> 
>      return str(self.ints[i])
> 
> 
> 
> In test methods, inject a mock input into the tested module with 
> 
> something like
> 
>          g.input = IntInput((5,3,2,1)).input
> 
> where the sequence passed is appropriate for the target and the response 
> 
> you want. This will be sufficient to test most of the operation of getguess.
> 
> 
> 
> (I am aware that some would say that IntInput should be a context 
> 
> manager with an exit method that restores g.input. I do not think that 
> 
> this complication is needed for this post.)
> 
> 
> 
> To test the getguess prompts and main output, collect output lines with 
> 
> something like
> 
> 
> 
> class Screen:
> 
>    def __init__(self):
> 
>      self.lines = []
> 
>    def print(self, line):
> 
>      self.lines.append(line)
> 
> 
> 
>    screen = Screen()
> 
>    g.input = IntInput((5,3,2,1), screen.print).input
> 
>    # Test that screen.lines is as it should be.
> 
>    # Be careful that actual and expected both have
> 
>    # or both do not have terminal \n.
> 
> 
> 
> For testing main, in test_xxx methods,
> 
>      screen = Screen
> 
>      g.print = screen.print
> 
>      # test screen.lines in
> 
> 
> 
> Another approach is to replace sys.stdin/out as is done in 
> 
> test.support.capture_stdin/out, but the latter are considered internal 
> 
> functions not for general use, and this method seems more complicated.
> 
> 
> 
> random and random.randint could be mocked, but this in not needed for 
> 
> this program with the randint call moved out of main().
> 
> 
> 
> ---
> 
> Terry Jan Reedy



More information about the Python-list mailing list