This is my first Stack overflow post and I hope to make a good impression.
I am working on a number guessing game in python to get my feet wet with unittest, but I am running into a silly conundrum.
The issue is that I want to create a unittest class that tests the following things
Does the pregenerated number match the guessed number
What does the program do if you enter in a letter
Do you see custom errors created when an error is occurring (in the try/except block)
My github project link is here -> https://github.com/robpedersendev/python-number-guessing-game
My main.py file is below
import random
import sys
def guessingGame(number, guess): # Answer and guess was placed here so as to allow the test to insert
# its own values into the game, but that is not working
while true:
try:
guess = input(
f"Number guesser.\t\t\n {name} choose a number between "
f"{start} and {end}. Enter guess? : ")
guess = int(guess)
if guess < number:
print("\n\nThat number is a tad low\n\n")
elif guess > number:
print("\n\nThat number is a tad high\n\n")
else:
print("\n\nCongrats, you chose the right number!\n\n")
break
except (TypeError, ValueError):
print("Sorry, numbers only please")
guess = str(f"\'{guess}\' Which is an invalid character")
continue
except (UnboundLocalError):
print("Sorry, numbers only please")
continue
finally:
print(f"{name}, your last successful guess was {guess}")
if __name__ == "__main__":
true = True
start = int(sys.argv[1])
end = int(sys.argv[2])
number = random.randrange(start, end) # Selects the winning number
name = input("\t\t\t\t\nHello stranger, whats your name? ")
guessingGame()
My test.py file looks like
import unittest
import main
class TestMain(unittest.TestCase):
def test_input(self):
true = True
number = 5
guess = 5
result = main.guessingGame(number, guess)
self.assertEqual(result)
if __name__ == "__main__":
unittest.main()
I keep getting the error
Traceback (most recent call last):
File "c:\Users\Robert\desktop\Side Projects\Udemy\Master python zero to Mastery\projects\python-number-guessing-game\test.py", line 10, in test_input
result = main.guessingGame(number, guess)
File "c:\Users\Robert\desktop\Side Projects\Udemy\Master python zero to Mastery\projects\python-number-guessing-game\main.py", line 6, in guessingGame
while true:
NameError: name 'true' is not defined
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
[Done] exited with code=1 in 0.375 seconds
Error message
I know I have much to do and much to learn, but I am hoping for some helpful guidance!
UPDATE:
Ok, I figured out the problem (kinda).
The issue is within my main.py file I need to have this
true = True
def guessingGame(number, guess, name='bob', start=1, end=20):
while true:
try:
# guess = input(
# f"Number guesser.\t\t\n {name} choose a number between "
# f"{start} and {end}. Enter guess? : ")
guess = int(guess)
if guess < number:
print("\n\nThat number is a tad low\n\n")
elif guess > number:
print("\n\nThat number is a tad high\n\n")
My test.py file
import unittest
import main
class TestMain(unittest.TestCase):
def test_input(self):
number = 5
guess = 5
start = 1
end = 20
name = "bob"
result = main.guessingGame(number, guess)
self.assertEqual(guess, number)
if __name__ == "__main__":
unittest.main()
I needed to predefine the values and comment out the first little block of code. Once I did that, my test passed!!
Any ideas, on how to get around this piece, or was this bad design on my part?
https://book.crawfordc.com/anatomy-of-a-modern-test-file
Here’s something I wrote a while back about writing unit tests with hypothesis and pytest.
For testing what goes wrong when you enter a letter you use the with pytest.raises pattern.
Using hypothesis to generate the random numbers will also work much better than your random number generation.
(You should just make the code while True: and not a variable. But you would need to return false if it failed. It would probably be better as a loop. )
Related
This is func_randomgame.py file. My main file
import random
def run_game(guess, answer):
if (0<guess<4):
if (guess == answer):
print('You Won!')
return True
else:
print('Try Again!')
else:
print("Hey Bozo! I said 1~3")
if __name__ == '__main__':
answer = random.randint(1,3)
while True:
try:
guess = int(input('Please enter a number between 1~3: ')
if (run_game(guess, answer)):
break
except (ValueError,TypeError):
print('Enter a number')
continue
This is my test file:
import unittest
import func_randomgame
class TestGame(unittest.TestCase):
def test_1(self):
result = func_randomgame.run_game(1, 1)
self.assertTrue(result)
def test_2(self):
result = func_randomgame.run_game(5,1)
self.assertFalse(result)
def test_3(self):
result = func_randomgame.run_game('5',1)
self.assertFalse(result)
if __name__ == '__main__':
unittest.main()
run_game(guess, answer) My function takes these two parameters. But test_3 is failing. If someone inputs string('5'), instead of a number result should be false and test_3 should be okay. But it is not the case. Why is this happening?
If someone inputs '5' as a string, the expression 0 < '5' < 4 fails with an exception:
TypeError: '<' not supported between instances of 'int' and 'str'
and thus your test is failing.
If you want your function to support string input, you should cast it before the comparison:
def run_game(guess, answer):
guess_as_number = int(guess)
if (0 < guess_as_number < 4):
if (guess == answer):
print('You Won!')
return True
else:
print('Try Again!')
else:
print("Hey Bozo! I said 1~3")
This will handle this specific case. But for a more robust code, I'd suggest to surround with try..except and handle unexpected inputs with informative error message to the user.
If someone inputs string('5'), instead of a number result should be false and test_3 should be okay. But it is not the case. Why is this happening?
If you want your test case to pass, you need to check for the type of the guess variable before starting your calculations in your function. A simple fix would be as below.
def run_game(guess, answer):
if type(guess) == int and (0<guess<4):
if (guess == answer):
print('You Won!')
return True
else:
print('Try Again!')
else:
print("Hey Bozo! I said 1~3")
But it is to be recommended that you refactor your function for proper error handling with try except blocks
first time posting here on Stackoverflow. I am currently learning python and am trying to make a simple game where the player will be shown a random 2 decimal place number and have to add up to 10. I want the game to only last 30 seconds, hence I added a clock function as well.
However, I am facing some troubles running both the clock and the game together at the same time. I have tried using threading but sadly it didn't work out. Appreciate all the help I can get!
import random
import time
import threading
number = 10.0
print("Sum up to 10!")
def game():
global score
score = 0
while True:
question = round(random.uniform(0.01, 9.99), 2)
while True:
print("\nYour number is: " + str(question))
try:
answer = float(input("\nAnswer: "))
if question + answer != number:
print("\nWrong answer! Try again!")
elif answer + question == number:
score += 1
print("\nCorrect!")
break
except ValueError:
print("Please key in a number(2 decimals)!")
def clock(countdown=0):
while countdown > 0:
time.sleep(1)
countdown -= 1
print("\rTime Left: " + str(countdown), end="")
if countdown == 0:
print("Final score: " + str(score))
quit()
clock_thread = threading.Thread(target=clock(31))
game_thread = threading.Thread(target=game())
clock_thread.start()
game_thread.start()
Your problem is probably on that line
clock_thread = threading.Thread(target=clock(31))
What you are doing right now is launching the function cloack with the argument 31, what you want to do is give a pointer to the function lock, and then give the argument 31
so you'd need to do something like that:
clock_thread = threading.Thread(target=clock, args=(31,))
args is a keyword same as target leading to the arguments you wanna give to the function(note that it has to be a tuple, ence why I wrote (31,)), and note that I didn't put the "()" after clock because I'm giving a pointer to the function clock, not launching the function clock
same, game_thread = threading.Thread(target=game()) becomes:
game_thread = threading.Thread(target=game) for the same reasons
I have written the following code which is working fine for me and I want to write the same code in a function (i.e., def function_name()) - which I have tried below as well but that's not a good approach and it's poorly written). So, how can I fix the code and how can I put input in an argument of my function name upon invoking/calling argument so that I can get my ultimate answer.
# Normal code:
from statistics import mean
lst = list()
while True:
user_int = input("Enter a number: ")
if user_int == "done":
break
try:
float_user_int = float(user_int)
except:
print("Bad input. Enter a valid number")
continue
lst.append(float_user_int)
print(mean(lst))
# Function Method I have tried which is working fine but it's poorly written and not yielding correct output. Rather, I am getting an error i.e., NameError: name 'float_user_input' is not defined. How can I write user_input integer numbers in an argument of my function name?
def avg_list(float_user_input):
from statistics import mean # import statistics
lst = list()
while True:
user_int = input("Enter a number: ")
if user_int == "done":
break
try:
float_user_int = float(user_int)
except:
print("Bad input. Enter a valid number")
continue # quit()
lst.append(float_user_int)
return(mean(lst)) # statistics.mean()
print(avg_list(float_user_input))
# This is the one I tried and it seems to be working with function but I don't think this code is perfect because I have done nothing in it except copy pasting the entire code under def function statement. How can I put user input number in an argument of function name?
def avg_list():
from statistics import mean # import statistics
lst = list()
while True:
user_int = input("Enter a number: ")
if user_int == "done":
break
try:
float_user_int = float(user_int)
except:
print("Bad input. Enter a valid number")
continue # quit()
lst.append(float_user_int)
return(mean(lst)) # statistics.mean()
print(avg_list())
Your help would be highly appreciated! Thanks.
Remove float_user_input from function declaration and function call
I think you want a function which takes input list from user such as get_input_list and calculate the mean as below:
from statistics import mean # import statistics
def avg_list(input_list):
return(mean(input_list))
def get_input_list():
# Take user input here
lst = list()
while True:
user_int = input("Enter a number: ")
if user_int == "done":
break
try:
float_user_int = float(user_int)
except:
print("Bad input. Enter a valid number")
continue
lst.append(float_user_int)
return lst
# call function
print(avg_list(get_input_list()))
I have this code. If you run it everything works fine if you follow the instructions. However I want to dummyproof it but when you enter too make troops and then you fix your mistake you get an error after fixing the mistake when functions restarts itself.
Please take a look and help me fix it.
import time
warriors = 100
def deploy(): #Calls fighters to front lines
amount = input('How many warriors would you like to send to the front lines? Your limit is %i warriors. Keep in mind that enemy invaders have been spotted inside your base. You must keep 10 warriors in base at all times. ' %(warriors))
try:
amount = int(amount)
except ValueError:
print('Please use numbers.')
time.sleep(1.5)
deploy()
if amount <= warriors:
print (type(amount))
elif amount > warriors:
print("You can't send that many warriors. You only have %i warriors." %(warriors))
time.sleep(1.5)
amount=0
deploy()
else:
print("You did something wrong. Try again.")
time.sleep(1.5)
deploy()
fighters = deploy()
warriors = warriors - fighters
You shouldn't use recursion (e.g. a function calling itself repeatedly) to try and do validation. For some general examples of good patterns for this, the canonical question is a good start. In your case I might refactor slightly.
import time
warriors = 100
def deploy():
while True:
amount = input("...") # your text here
try:
amount = int(amount)
except ValueError:
print("Please use numbers.")
# move the time.sleep to the end
else: # only execute if the try block succeeds
if amount > warriors:
print("You can't send that many warriors. "
"You only have %i warriors." % warriors)
else:
# everything went right!
print(type(amount)) # why are you doing this...?
return amount # did you forget this in your sample code?
# if you get here: something broke
time.sleep(1.5)
That said, this is kind of ugly since it's so deeply nested. Remember the Zen: "Flat is better than nested." Let's refactor a bit to make a new function that does the validation for us.
import time
warriors = 100
def int_less_than(prompt, ceil, type_error_msg=None,
value_error_msg=None, callback=None):
"""Returns a validated integer
input(prompt) must be less than ceil. Print to console a std error msg
if none is specified. If you specify a callback: run the callback if any
errors are detected.
"""
while True:
user_in = input(prompt)
try:
user_in = int(user_in)
except ValueError:
print(type_error_msg or "You must enter a number")
else:
if user_in > ceil:
print(value_error_msg or "You must enter a number "
"less than {}".format(ceil))
else:
return user_in
if callback is not None:
callback() # lets us insert a time.sleep call
def deploy():
amount = int_less_than("How many warriors would you like to...",
warriors,
callback=lambda: time.sleep(1.5))
return amount
So, I'm just fooling around in python, and I have a little error. The script is supposed to ask for either a 1,2 or 3. My issue is that when the user puts in something other than 1,2 or 3, I get a crash. Like, if the user puts in 4, or ROTFLOLMFAO, it crashes.
EDIT: okay, switched it to int(input()). Still having issues
Here is the code
#IMPORTS
import time
#VARIABLES
current = 1
running = True
string = ""
next = 0
#FUNCTIONS
#MAIN GAME
print("THIS IS A GAME BY LIAM WALTERS. THE NAME OF THIS GAME IS BROTHER")
#while running == True:
if current == 1:
next = 0
time.sleep(0.5)
print("You wake up.")
time.sleep(0.5)
print("")
print("1) Go back to sleep")
print("2) Get out of bed")
print("3) Smash alarm clock")
while next == 0:
next = int(input())
if next == 1:
current = 2
elif next == 2:
current = 3
elif next == 3:
current = 4
else:
print("invalid input")
next = 0
Use raw_input() not input() the latter eval's the input as code.
Also maybe just build a ask function
def ask(question, choices):
print(question)
for k, v in choices.items():
print(str(k)+') '+str(v))
a = None
while a not in choices:
a = raw_input("Choose: ")
return a
untested though
since the input() gives you string value and next is an integer it may be the case that crash happened for you because of that conflict. Try next=int(input()) , i hope it will work for you :)