What kind of unittests make sense for a chatbot in python? - python

I am creating a simple chatbot at the moment and now I would like to test it using the unittests package. I am new to programming and I have only recently heard about the existence of unit tests. I am thinking that for a chatbot to be tested, it would make sense to see if it gives the right output to the right user input. So if the user writes "Hi", for example, it should output "Hey what's up?" as specified in the library (I have written a bunch of libraries for it; it is not a deep-learning-kinda-bot).
This is the code of my chatbot:
# importing regular expression operations, which we need later to access the libraries
import re
import random # later we want to pick a chatbot answer to a statement by random
from Jordan_Library import dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona # import libraries
# and here we imported all the topic libraries
class JordanBot:
"""
A class that represents the abilities of Jordan the chatbot.
...
Attributes
----------
name : str
user can insert name, which will later be shown in the chat.
Methods
----------
name : str
user can insert name, which will later be shown in the chat.
"""
def __init__(self, name): # this function is always called when a new object of the class is called
"""
Constructing topics for Jordan. Takes the user's name as an argument.
"""
self.name = name
def chat(self):
"""
A function that enables chatting with Jordan after picking a topic.
Take no arguments.
"""
topics = [dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona]
while True:
print("Welcome. My name is Jordan. You can now choose a topic that we can talk about.")
print("Press '0' to have some good old-fashioned smalltalk.")
print("Press '1' to tell me about your deepest sorrows and your destroyed soul.")
print("Press '2' to curse with me.")
print("Press '3' to meditate with me.")
print("Press '4' to talk about Corona.")
# execute this welcome text and tell user what to press in order to pick a topic.
choice = input(">>> ")
# initialize input
if choice == '0': # determine which key to press to initiate the specific topic
print("Alrighty, let's do some chitchatting.")# initiate welcome text for specific topic
print("Don't forget that I am sensitive to punctuation.")
elif choice == '1':
print("Please tell me about your deepest sorrows and your destroyed soul.")
print("Don't forget that I am sensitive to punctuation.")
elif choice == '2':
print("Make yourself ready. let's insult each other!")
print("Don't forget that I am sensitive to punctuation.")
elif choice == '3':
print("Ok then, let's meditate.")
print("Don't forget that I am sensitive to punctuation..")
elif choice == '4':
print("Let's talk about Corona.")
print("Don't forget that I am sensitive to punctuation..")
elif choice == 'q': # if user wants to quit
break
else: # if user pressed the wrong key.
print("Try again.")
edition = topics[int(choice)]
statement = list(map(lambda x:re.compile(x[0], re.IGNORECASE), edition))
# list(map()) applies a function to all elements of a specified object, in this case the cursing library
# lambda makes sure that re.compile is applied in a certain way to all elements of the library without being case-sensitive
# re.compile makes sure that the elemets are turned into objects that can be matched later to another item in the library
answer = list(map(lambda x:x[1], edition))
# same here, but here we pick the second argument in the list x[1], which entails Jordan's answers
while True:
userInput = input(' ' + self.name + ' >>> ') # this allows for the user to type in keys
resp = "I did not understand what you said. Also, I am sensitive to punctuation." # initalize response variable
counter = 0 # initalize counter
while resp == "I did not understand what you said. Also, I am sensitive to punctuation." and counter < len(statement): # determine when to start my loop
for i in range(0, len(statement)): # loop through the indexed library
match = statement[i].match(userInput) # look if input of the user matches with one of the words in the library
if match:
word = statement[i].split(userInput)[1] # We have to take the first element of this list
resp = random.choice(answer[i]) # if there is a match, pick a random answer from x[1]
counter += 1 # make sure that the counter is now + 1 so it does not write the initialized response from the beginning but continues with the loop
# if there is no match though, then it will write the initialized answer
if userInput == 'q':
print(random.choice(answer[i]))
print("---------------------------------")
print("Do you want to choose another topic? Pick below or press 'q' to quit for realsies.")
print("---------------------------------")
break
resp = resp.format(word)
print('____________')
print(' ')
print('Jordan >>> ' + resp) # print Jordan's answer
The unittests I am trying to create are something like:
import unittest
from Jordan_Library import dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona # import dictionairies
from Jordan_Class import JordanBot
class testJordan(unittest.TestCase):
"""
A class to test several units of Jordan to make sure everything is working correctly.
"""
def test_match(self):
j = JordanBot('StringName')
j.chat()
user_input = "What are you?"
bot_output = list(map(lambda x:x[1], dict_smalltalk)) # this is how I access the right response in the right library
matched = bot_output.match(user_input)
self.assertIn("I am a chatbot, dude. What do you think?", matched)
def test_regex(self):
j = JordanBot('StringName')
j.chat()
text = 'something'
regex = {}
self.assertRegexpMatches(text, regex)
if __name__ == '__main__':
unittest.main()
The main problem is that these lines only run my code, but not really any unit tests. Advice is much appreciated!

Related

What is the best practice for organizing a piece of reusable code?

I am building a text-based encryption and decryption game. There are different levels, and each level uses a different cipher for encrypting a text. I am trying to figure out the best practice for the series of questions and prompts (the narrative) I give the user to determine if he wants to practice, do the test, encrypt, or decrypt. 90% of the narrative is the same for each level, so I don't want to repeat myself with identical code. What is the best way to do this?
My first thought was to define a function that contained the general script, and to call the specific functions as parameters. (This is what I have attempted to do below). But I seem to run into a scope problem. When I call the caesar() function as one of the arguments in the script() function, I need to enter the text to be encrypted, but this text isn't provided by the user until the script() function has already started running.
Should I be using a class to define the narrative portion of the program, and then inherit to more specific types?
Or should I just repeat the narrative code at the different levels?
Here is the narrative script():
def script(encrypt, decrypt):
"""Asks user if they want to practice (encode or decode) or take the
test, and calls the corresponding function."""
encrypt = encrypt
decrypt = decrypt
while True:
print('Type Q to quit. Type M to return to the main menu.')
prac_test = input('would you like to practice or take the test? P/T')
if prac_test.lower() == 'p':
choice = input('Would you like to encrypt or decrypt? E/D ')
if choice.lower() == 'e':
text = input('Enter the text you would like to encode: ')
encrypt
elif choice.lower() == 'd':
text = input('Enter the text you would like to decode: ')
key = int(input('Enter the key: '))
decrypt
else:
print('You must enter either "E" or "D" to encode or decode a
text. ')
elif prac_test.lower() == 't':
text = random.choice(text_list)
encrypted_text = encrypt
print(encrypted_text[0])
answer = input('s/nCan you decode this string? ')
if answer.lower() == ran_str.lower():
print('Congrats! You solved level 1!\n')
pass
elif answer != ran_str:
print("Sorry, that's not correct. Why don't you practice some
more?\n")
script(encrypt, decrypt)
elif prac_test.lower() == 'q':
exit()
elif prac_test.lower() == 'm':
break
else:
print('Please enter a valid choice.')
Here is one of the levels using a caesar cipher:
def caesar(mode, text, key=None):
"""
...
The dictionaries that convert between letters and numbers are stored in the .helper file, imported above.
"""
mode = mode
if mode == 'encrypt':
key = random.randint(1, 25)
elif mode == 'decrypt':
key = key
str_key = str(key)
text = text.lower()
# converts each letter of the text to a number
num_list = [alph_to_num[s] if s in alph else s for s in text]
if mode == 'encrypt':
# adds key-value to each number
new_list = [num_to_alph[(n + key) % 26] if n in num else n for n in
num_list]
elif mode == 'decrypt':
# subtracts key-value from each number
new_list = [num_to_alph[(n - key) % 26] if n in num else n for n in
num_list]
new_str = ''
for i in new_list:
new_str += i
return new_str, str_key
And here is who I would try to use them together:
script(caesar('encrypt' text), caesar('decrypt', text, key))
Please instruct me on the best way to organize this reusable narrative code.
You probably want to use multiple functions:
One, that we will call main(), to display the menu and interact with the user
A class Caesar, that expose two functions: encrypt(text, key) and decrypt(text, key)
A simple program could then look like
def main():
print("Welcome to the game")
action = input("Would you like to encrypt or decrypt a text [e/d]">).lower()
text = input("What is the text you want to test on ? >")
key = input("What's your key")
# optionnaly, ask for what kind of cipher they want to use, then use a dict to chose the right class
cipher = Caesar()
if action == "e":
output = cipher.encrypt(text, key=key)
else:
output = cipher.decrypt(text, key=key)
print(output)
print("Thanks for playing!")

Recursion with defined functions for simple text-based Python program

I'm writing my first program - it's an idiom generator, which combines individual elements from lists of random verbs, nouns, and pronouns (that I have entered) in Madlibs style and generates a humorous expression. This is a simplified version of my source code:
baseFunction = True
def mainFunction() :
import random
quest = input("Which language do you want it in? Type 'French' or 'English'. ")
if quest == "French" or "french":
verb =
#list of verbs I have manually entered
noun =
#list of nouns I have manually entered
pronoun =
#list of pronouns I have manually entered
morenouns =
#list of nouns I have manually entered
phrase = random.choice(verb) + random.choice(noun) + random.choice(pronoun) + random.choice(morenouns)
print(phrase)
print("Now, give it some meaning and use in the world!")
elif quest == "English" or "english":
verb =
#another list of verbs I have manually entered
noun =
#another list of nouns I have manually entered
pronoun =
#another list of pronouns I have manually entered
morenouns =
#another list of nouns I have manually entered
phrase = random.choice(verb) + random.choice(noun) + random.choice(pronoun) + random.choice(morenouns)
print(phrase)
print("Now, invent some meaning for it and use it in the world!")
f8 = input("Do you want to make another one? Say 'yes' if you do. ")
if f8 == "yes" or "Yes":
mainFunction()
else:
print("Thanks for playing!")
else:
print("Didn't quite catch that. Try again! (say yes!)")
mainFunction()
def malif() :
ques = input("Want to hear a funny idiom? Say 'yes' or 'no'. ")
if ques == "yes" or "Yes":
mainFunction()
elif ques == "no" or "No":
print("Wrong answer. Try again! (say yes)")
malif()
else:
print("Didn't quite catch that. Say 'yes' or 'no'.")
while baseFunction :
malif()
mainFunction()
Essentially, I am asking the user whether they want to make an idiom, offering them a choice of language, generating the expression for them, and then asking them if they want to repeat the process. When I run the script in PyCharm, it runs the two functions in order (meaning, malif() first and then mainFunction(), as I have it at the end) but it does not pay any attention to my input (ex. if I say 'no' it runs the mainFunction anyway and will always do it in French even if I say 'English').
I used some of the tips discussed in this entry (Python - How to make program go back to the top of the code instead of closing). I think the problem lies calling the functions in their own definitions (ex. calling malif() if I answer 'no' to input 'ques', which is defined in malif() ). Yet, I have followed the tips discussed in the question that I linked and it is still not working the way that I want it to. Am I doing something wrong in formatting the code (ex. in terms of indentation) or if it is not obvious what I am doing wrong, is there a way for me to loop functions back to the beginning that was not suggested in the original question?
Thanks!
First some tips when you work with strings as input. Python will make the difference between caps and non-caps letter, thus a good way to deal with strings is to lower() them first (or upper(), ...):
Example:
ques = input("Enter Yes or No: ")
ques = ques.lower()
if ques == "yes":
# do something
elif ques == "no":
# do something else
else:
# raise error
Now I feel like your code is build in a funny way. A good habit is to separate the import from the functions, from the main program. The 2 first will be imported if the module (file) is imported, while the last one will be played when the file is executed. To do so, you can use this:
# -*- coding: utf-8 -*-
"""
docstring of the module
"""
# Imports
import random
import os
# Functions
def f():
return "Hello world"
# Main program
if __name__ == '__main__':
# Calling the function, taking the inputs and so on
In the main program, it's rather useful to deal with the possibility that an exception is raised. Moreover, if you use the cmd to display your program, the cmd will close immediately when an error is raised. This syntax is quite useful:
if __name__ == '__main__':
try:
# Do stuff
except:
import sys
print (sys.exc_info()[0])
import traceback
print (traceback.format_exc())
os.system("pause") # for windows, else easy way is to have an empty input to freeze the cmd
Now your code. I would rework it this way:
# -*- coding: utf-8 -*-
"""
Docstring
"""
# Imports
import random
import os
# Functions
def build_a_phrase(language) :
if language == "french":
verb = ["vendre", "atterir", "attaquer", "jeter"]
#list of verbs I have manually entered
noun = ["arbre", "poisson", "chien"]
#list of nouns I have manually entered
pronoun = ["un", "les"]
#list of pronouns I have manually entered
morenouns = ["chat", "oiseau"]
#list of nouns I have manually entered
choices = [random.choice(verb), random.choice(noun), random.choice(pronoun), random.choice(morenouns)]
phrase = " ".join(choices) # Add a space between the words
return phrase
elif language == "english":
verb = ["...", "...", "..."]
#another list of verbs I have manually entered
noun = ["...", "...", "..."]
#another list of nouns I have manually entered
pronoun = ["...", "...", "..."]
#another list of pronouns I have manually entered
morenouns = ["...", "...", "..."]
#another list of nouns I have manually entered
choices = [random.choice(verb), random.choice(noun), random.choice(pronoun), random.choice(morenouns)]
phrase = " ".join(choices) # Add a space between the words
return phrase
if __name__ == '__main__':
try:
# Parameters
available_language = ["french", "english"]
available_answers = ["yes", "no"]
# Safety implementation of an input
quest = ""
i = 0
while quest.lower() not in available_answers:
quest = input("Want to hear a funny idiom? Say 'yes' or 'no'. ")
i += 1
if i == 2: # number of tries
break
if quest.lower() == "no":
print ("I'm sure you meant yes.")
language = ""
i = 0
while language.lower() not in available_language:
language = input("Which language do you want it in? Type 'French' or 'English'.\n")
i += 1
if i == 2: # number of tries
break
while True:
sentence = build_a_phrase(language)
print (sentence)
print ("Now, give it some meaning and use in the world!")
f8 = ""
i = 0
while f8.lower() not in available_answers:
f8 = input("Do you want to make another one? Say 'yes' if you do. ")
i += 1
if i == 2: # number of tries
break
if f8.lower() == "no":
print("Thanks for playing!")
break
except:
import sys
print (sys.exc_info()[0])
import traceback
print (traceback.format_exc())
os.system("pause")
Hope you'll get a few good tricks from this answer, and some good habits :)
Not complete yet, when the input is wrong, an Error should be raised rather than waiting for the error resulting in the wrong input (i.e. a raise statement should be placed instead of the breaks)

complex iteration over a string in python

The aim of this code is to go through the users input check if check if any of the words match the words on the dictionary then give one response related to the first word that matches and if not reply with "I am curious tell me more". My problem is that I can't iterate over the list and print a single response.
def main():
bank = {"crashed":"Are the drivers up to date?","blue":"Ah, the blue screen of death. And then what happened?","hacked":"You should consider installing anti-virus software.","bluetooth":"Have you tried mouthwash?", "windows":"Ah, I think I see your problem. What version?","apple":"You do mean the computer kind?","spam":"You should see if your mail client can filter messages.","connection":"Contact Telkom."}
def welcome():
print('Welcome to the automated technical support system.')
print('Please describe your problem:')
def get_input():
return input().lower().split()
def mainly():
welcome()
query = get_input()
while (not query=='quit'):
for word in query:
pass
if word in bank:
print(bank[word])
elif not(word=="quit"):
print("Curious, tell me more.")
query = get_input()
mainly()
if __name__=="__main__":
main()
In your code there is few mistakes. First one, when you start the script you run main which loading a local disctionnary 'bank' which does't exist out of the function. When the function end, it runs 'mainly' but doesn't remember the dictionary.
Second one, as you use a dictionnary structure you don't need to loop thru and check all elements 1 by 1. You can instead use the function dict.get
I can propose you this solution :
def welcome():
print('Welcome to the automated technical support system.')
print('Please describe your problem:')
def get_input():
return input().lower().split()
def main():
bank = {"crashed": "Are the drivers up to date?", ...}
welcome()
query = get_input()
while query != 'quit':
if bank.get(query, None) is not None:
print(bank[query])
else:
print("doesn't exist")
query = get_input()
print("Curious, tell me more.") # will be triggered only when you are out of the loop
if __name__=="__main__":
main()
In that case bank.get(query, None) will return the sentence if the word exist, else it returns None.
You can also simplify it as :
while query != 'quit':
sentence = bank.get(query, "doesn't exist")
print(bank[query])
query = get_input()
this is because if it exist, sentence = what you want to display, if it doesn't, it displays the error message you want
I hope it helps,

Start() is not defined

Hey I am trying to create a simple text based slot machine with the view to convert it into a graphical one latter.
I have started by it prompting a menu which works fine. However when the user enters the required 'p' to continue it won't call the next function because I haven't defined it yet.... I have?
from time import sleep
from random import shuffle
#Creates the class
class Machine():
#This is the constructor full of attributes
def __init__(self):
self.reel1 = ["Lemon", "Bell", "Cherry"]
self.reel2 = ["Lemon", "Bell", "Cherry"]
self.reel3 = ["Lemon", "Bell", "Cherry"]
firstSlide = self.reel1
secondSlide = self.reel2
thirdSlide = self.reel3
self.currentFunds = "10"
funds = self.currentFunds
f = open('score.txt', 'w')
f.write(funds)
#Dictates all the funds and checks if the user has enough money or needs to add money
def Funds(self):
if self.currentFunds == "0":
print("You are out of credits! :( \n")
Menu()
#Starts the spinning and randomizes the lists
def Start(self, firstSlide, secondSlide, thirdSlide):
shuffle(firstSlide, secondSlide, thirdSlide)
print(firstSlide[0], secondSlide[1], thirdSlide[3])
#Intro Menu to give player stats and options
def Menu(self):
play = ""
m = Machine()
print('*****************\n')
print(' WELCOME! \n')
print('*****************\n')
print('Current Credits: ', m.currentFunds)
if input("Press P to play \n") == "P" or "p":
machine = Start()
machine.Start()
machine = Machine()
while True:
machine.Menu()
Any ideas?
You have Start as a member function of the Machine class. You need to replace machine = Start() with self.Start().
It actually looks like this is the case with a number of the variables you seem to be trying to use. For example, I would expect that Start would rely on self.start, but it is relying on parameters (which you are not passing in).
As a general comment on this code, I'm wondering if you really need/want to have this be structured this way. You seem to be creating the object recursively and I think you might be better off restructuring a bit.

Python- how to init random function

I am a beginer python learner. I am trying to create a basic dictionary where random meaning of words will come and user have to input the correct word. I used the following method, but random doesn't work. I always get the first word first and when the last word finishes, I get infinite 'none' until I kill it. Using python 3.2
from random import choice
print("Welcome , let's get started")
input()
def word():
print('Humiliate')
a = input(':')
while a == 'abasement':
break
else:
word()
# --------------------------------------------------------- #
def word1():
print('Swelling')
a = input(':')
while a == 'billowing':
break
else:
word()
# ------------------------------------------------------------ #
wooo = [word(),word1()]
while 1==1:
print(choice(wooo))
is there any faster way of doing this and get real random? I tried classes but it seems harder than this. Also, is there any way I can make python not care about weather the input is capital letter or not?
To answer one part of your question ("is there any way I can make python not care about weather the input is capital letter or not?"): use some_string.lower():
>>> "foo".lower() == "foo"
True
>>> "FOO".lower() == "foo"
True
An this is to help you how you could improve the structure of your code:
import sys
from random import choice
WORDPAIRS = [('Humiliate', 'abasement'), ('Swelling', 'billowing')]
def ask():
pair = choice(WORDPAIRS)
while True:
answer = raw_input("%s: " % pair[0]).lower()
if answer == pair[1]:
print "well done!"
return
def main():
try:
while True:
ask()
except KeyboardInterrupt:
sys.exit(0)
if __name__ == "__main__":
main()
It works like that:
$ python lulu.py
Swelling: lol
Swelling: rofl
Swelling: billowing
well done!
Humiliate: rofl
Humiliate: Abasement
well done!
Swelling: BILLOWING
well done!
Humiliate: ^C
$
wooo = [word, word1]
while 1:
print(choice(wooo)())
But in any case it will print you None, cause both of your functions return nothing (None).

Categories