lilgallon
lilgallon

Reputation: 625

How to test a function that is using user-interface

I am trying to test a function that needs an interaction of the user. The question is how can I do it programmatically ?

Here is an example where we ask for the user to select an item in a list (main.py) :

import tkinter as tk
from tkinter import Button, OptionMenu, StringVar


def ask_for_item_in_list(lst, title, default_index=0):

    root, item = tk.Tk(), None
    WIDTH, HEIGHT = 300, 120

    root.title(title)

    root.maxsize(width=WIDTH, height=HEIGHT)
    root.minsize(width=WIDTH, height=HEIGHT)
    root.resizable(0, 0)

    variable = StringVar(root)
    variable.set(lst[default_index])

    option_menu = OptionMenu(root, variable, *lst)
    option_menu.pack(fill="none", expand=True)

    def on_close():
        # The window has been closed by the user
        variable.set(None)
        close()

    def close():
        # It quits mainloop()
        root.quit()
        # It closes the window
        root.destroy()

    button_ok = Button(root, text='OK', command=close)
    button_ok.pack(fill='none', expand=True)

    root.protocol('WM_DELETE_WINDOW', on_close)

    # Execution stops here as long as the user has not closed the window or
    # pressed ok
    root.mainloop()

    # We retrieve the selected item
    item = variable.get()
    if item == 'None':
        item = None

    return item

if __name__ == '__main__':
        lst = ['Item 1', 'Item 2', 'Item 3']
        title = 'Select an item'
        default_selected_idx = lst.index('Item 2')

        selected_item = ask_for_item_in_list(lst, title, default_selected_idx)

        print(selected_item)

I used pytest to write all my tests since I can't use object oriented programming. Actually, the code must be maintainable by people who are not professional developpers.

As you can see, I can't test this function this way, since it will wait for the user input (test_main.py):

from main import ask_for_item_in_list


def test_ask_for_item_in_list():
    lst = ['Item 1', 'Item 2', 'Item 3']
    title = 'Select an item'

    # Here TRY to test if changing the default selected index works
    default_selected_idx = lst.index('Item 2')

    # Code to simualte that the user as clicked on OK ?
    # user.click_button('OK') ?
    selected_item = ask_for_item_in_list(lst, title, default_selected_idx)

    assert selected_item == 'Item 2'

I have faced this problem many times (whatever is the language used), I would like to know how this is supposed to be done in a clean way.

Thanks for reading ! :)

Upvotes: 2

Views: 223

Answers (1)

YesThatIsMyName
YesThatIsMyName

Reputation: 1636

Usually one fills the user input beforehand with expected or special values and then calls the test function several times. Also you may simulate clicks with various tools.

In C++ you could do something like:

int number_of_tests = 10;
int tests_passed = 0;
tests_passed += my_test_function_int(0);
tests_passed += my_test_function_int(-1);
...
tests_passed += my_test_function_string("foo");
tests_passed += my_test_function_string("");
tests_passed += my_test_function_string(" ");
...
return (tests_passed == number_of_tests); 

This is just an example how one can do it (in our company we do it this way). Also it is not very hard to add new tests for non-programmers or new people.

Upvotes: 2

Related Questions