-
Notifications
You must be signed in to change notification settings - Fork 16
Description
The goal is to have an additional test inside src/test_menu.py, that makes sure:
- when a user selects the 'Edit Resume' option from the main menu (this option is the first option in the menu, but it only becomes available when the file in
RESUME_PATH(eg.temp_test_resume.txt), exists and contains some text (the resume)- the resume text inside
RESUME_PATHis displayed on screen - there are directions at the top of the screen ("Base Resume (Press 'q' to go back, 'r' to replace):")
- when the user presses 'q'
- the screen goes back to the menu
- the resume text inside
This can be done by creating a method test_displaying_resume(self, mock_getenv, mock_curses) to test the workflow above in the app
Let's check the code of the current test_manage_resume method as a base example. In there we find the following (open https://github.com/nicobrenner/commandjobs/blob/master/src/test_menu.py if you want to see the whole code while following along the sections below):
This first part:
- defines the test method and
- defines a mock method for environment variables, what this does is that when the app calls
os.getenv('OPENAI_API_KEY'), instead of reading the environment variables from the terminal, it will get the value defined in the mock (in this case, 'test_key')
def test_manage_resume(self, mock_getenv, mock_curses):
# Mock environment variables
mock_getenv.side_effect = lambda x: {'OPENAI_API_KEY': 'test_key', 'BASE_RESUME_PATH': 'temp_base_resume.txt', 'HN_START_URL': 'test_url', 'COMMANDJOBS_LISTINGS_PER_BATCH': '10', 'OPENAI_GPT_MODEL': 'gpt-3.5'}.get(x, None)Below, the same concept of creating a mock, but for ncurses and stdscr (which MainApp uses to create the user interface)
# Mock stdscr object
mock_stdscr = MagicMock()
mock_curses.initscr.return_value = mock_stdscr
mock_stdscr.getmaxyx.return_value = (100, 40) # Example values for a terminal sizeThe following part checks if the test base resume exists, and if so, deletes it to have the right environment for the test
# This is testing when the resume file doesn't exist
# Remove test resume file, to make sure it doesn't exist
temp_test_resume_path = os.getenv('BASE_RESUME_PATH')
if os.path.exists(temp_test_resume_path):
os.remove(temp_test_resume_path)In the case of the test for displaying the resume, you should just directly write to the temp_test_resume_path some test text, then have the test go through the app and display the text. The code below reads a sample resume into a test_resume_text variable
# Use config/base_resume.sample as the test resume
test_resume_text = ''
with open('config/base_resume.sample', 'r') as file:
test_resume_text = file.read()To simulate the input for the app, the input needs to be mocked. The code below can mock 2 different sequences of input, the input for getch, which is the functions that listens for keyboard input when the app is displaying the main menu, and the input for get_wch which supports utf8 and is the method that captures the resume text when the user pastes it into the terminal
These sequences of inputs will be consumed/run in order, from left to right, or from beginning to end
You'll notice the input sequence for getch is commented out and is there only as an example, given that the test will skip the main menu when calling the manage_resume() method directly. Hence this test only needs to simulate the get_wch input of pasting the resume text and then Esc at the end (['\x1b'])
# Mock user input sequence for getch and get_wch
# Presses Enter (10) to go into Paste Resume option
# mock_stdscr.getch.side_effect = [10]
# Paste the resume text + Esc ('\x1b'), to save the resume
mock_stdscr.get_wch.side_effect = list(test_resume_text) + ['\x1b']Now here the test calls the part of the app that it wants to test. When menu.manage_resume() is called, that part of the application is executed and the application uses the mocked functions, including the mocked input. In this case, the method shows the interface for pasting the resume for the first time, and the test pastes the test resume text, then presses Esc and finally checks for the exit_message it gets back from manage_resume()
The exit_message should be "Resume saved to ..." and include the path of the test resume, and so the assertEqual() method checks that's the case, and it throws an error otherwise. The assert methods are what determines if the test passes or not. For a test to pass, all the assertions need to pass
# Simulate calling capture_text_with_scrolling
exit_message = menu.manage_resume(mock_stdscr)
# Verify we got a success message
self.assertEqual(exit_message, f'Resume saved to {temp_test_resume_path}')There is a second assertion here, and it is checking that the saved text is the same as originally pasted into the application
# Verify the text was saved to base_resume.txt
with open(temp_test_resume_path, 'r') as file:
saved_text = file.read()
self.assertEqual(saved_text, test_resume_text)Finally, do some cleanup, removing temporary files used during the test
# Remove temp test resume file
if os.path.exists(temp_test_resume_path):
os.remove(temp_test_resume_path)
temp_test_db_path = DB_PATH
if os.path.exists(temp_test_db_path):
os.remove(temp_test_db_path)You can run the current test with: python src/test_menu.py
I also recommend feeding this ticket + the code in src/test_menu.py + the code in src/menu.py to chatgpt and ask it to provide some guidance, or even some code that you can try out