0

I created a python unit test for my word occurence Gui project, I want to test the occurrence of the top 5 words so it should return a true value however I can't figure out how to run the unit test? I'm trying to use idle shell but should I use the visual studio command prompt instead or is the problem with my unit test not being set up properly? I'll display the code below in case you need it for the task:

#Imports
import tkinter as tk
from tkinter import *
from tkinter import filedialog
from collections import Counter
from tkinter import messagebox
import collections
import unittest 

# Initialize the dictionary
wordcount = {}

#Unit Test
class TestWordCount(unittest.TestCase):
    def test_count_words(self):
        n_print = 5
        expected_result = {
            'the' : 731,
            'and' : 565,
            'to' : 379,
            'of' : 342,
            'i' : 313
        }

        counter = n_print(int)
        result = counter.count_words()
        assert len(result) == len(expected_result)
        assert result == expected_result
        unittest.Word_Occurence_GUI().run(TestWordCount())
        

#open Macbeth text file
file = open('Macbeth Entire Play.txt', encoding="utf8")
a= file.read()

class Application(tk.Frame):
    def __init__(self, master):
        super().__init__()  # Call __init__() method in parent (tk.Frame)
        
        self.label = tk.Button(self, text='How many words to Sort?', command=self.ask_count)
        self.label.grid(row=0)
        self.open_btn = tk.Button(text='Compute', command=self.ask_count)
        self.open_btn.pack(pady=(30,10))
        self.exit_btn = tk.Button(text='Exit', command=master.destroy)
        self.exit_btn.pack()

    def ask_count(self):
        
        with open('Macbeth Entire Play.txt', encoding="utf8") as file:
            self.file_text = file.read()
        for word in a.lower().split():
          word = word.replace(".","")
          word = word.replace(",","")
          word = word.replace(":","")
          word = word.replace("\"","")
          word = word.replace("!","")
          word = word.replace("“","")
          word = word.replace("‘","")
          word = word.replace("*","")
          if word not in wordcount:
              wordcount[word] = 1
          else:
              wordcount[word] += 1
        n_print = int(input("How many most common words are: "))
        print("\nThe {} most common words are as follows\n".format(n_print))
        word_counter = collections.Counter(wordcount)
        for word, count in word_counter.most_common(n_print):
          print(word, ": ", count)
        messagebox.showinfo("Top words...", "The top words are: \n" + "\n".join([(str(word)+": "+str(count)) for word, count in word_counter.most_common(n_print)]))

        # Close the file
        file.close()
        messagebox.showinfo("The top words are: ")

if __name__ == '__main__':
    root = tk.Tk()
    root.title("Count words")
    root.geometry('400x400+900+50')
    app = Application(root)
    app.pack(expand=True, fill='both')
    root.mainloop()
    #run unit test
    unittest.main()

  • Your code shouldn't be effected by IDLE/Visual studio. If it is then that means that that program has a bug. – TheLizzard Mar 04 '21 at 17:28
  • Tkinter application are run by calling the `mainloop()` function, which generally doesn't return until the user quits the program. The way your code is written, the `unittest.main()` doesn't occur until then. To do what you want will require modifying the GUI so that some user event — such as clicking on a `Button` — causes the `unittest` function to be called. – martineau Mar 04 '21 at 17:48
  • A potential additional problem is that Tkinter GUI programs often don't work well with IDLE because IDLE itself is a Python/Tkinter program — so it can "hang" if you use it to run your own Tkinter application. – martineau Mar 04 '21 at 17:56
  • @TheLizzard: That's not true — see my previous comments. – martineau Mar 04 '21 at 17:59
  • @martineau I have been using IDLE for more than half of my tkinter coding and it has never crashed. IDLE also runs the user code in a separate thread according to an IDLE developer [here](https://stackoverflow.com/questions/66286367/why-is-my-function-faster-than-pythons-print-function-in-idle#comment117226660_66298508). If you start a new `tk.Tk` window in a new thread, it will create its own tcl interpreter so it shouldm't interfere with the user's code – TheLizzard Mar 04 '21 at 18:06
  • @TheLizzard: I'll only say I was only paraphrasing what the first result of this [Google query](https://www.google.com/search?lr=&hl=en&as_qdr=all&ei=Zh5BYNzlINTz-gThwaKADQ&q=python+can+%22IDLE%22+be+used+to+develope+tkinter+applications&oq=python+can+%22IDLE%22+be+used+to+develope+tkinter+applications&gs_lcp=Cgdnd3Mtd2l6EAw6BwgAEEcQsANQ2mFY1H5g644BaAFwAXgAgAHnCIgB5QqSAQcwLjIuNy0xmAEAoAEBqgEHZ3dzLXdpesgBCMABAQ&sclient=gws-wiz&ved=0ahUKEwjcsICRmpfvAhXUuZ4KHeGgCNAQ4dUDCAw) says. So if what you claim is true, what's wrong with the code in the OP's question? – martineau Mar 04 '21 at 18:15
  • @Fallen Dionysus: I would suggest first getting your unittest working _without_ using tkinter to determine if it's setup correctly — then when you're sure it is, work on getting it to run from within your GUI (based on the suggestion I made in my initial comment). – martineau Mar 04 '21 at 18:36
  • @martineau by the way I still don't know how unit tests work. I usually test the program by hand. I am only able to help with tkinter/IDLE stuff. – TheLizzard Mar 04 '21 at 18:51
  • @TheLizzard: I'm no `unittest` expert but can tell by looking the code in `class TestWordCount` is wrong in the sense that it won't even run. Fallen: Please follow my advice about getting a valid unit test setup first before trying to deal with Tkinter issues (if there are beyond what I said regarding how `mainloop()` works). Part of the problem here is you're essentially asking two questions and should instead concentrate on one and provide a runnable [mre]. – martineau Mar 04 '21 at 19:21
  • @martineau can you explain how to get it to work correctly? – Fallen Dionysus Mar 04 '21 at 21:03
  • @Fallen: No. If you can't figure out how to do it I suggest you ask a new question about just that part. Try to make it clear _what_ you're testing—whether `collections.count` works or is it something else? Also suggest testing with a much smaller amount of input data, so you can calculate the results manually to determine what they should be. – martineau Mar 04 '21 at 22:33
  • @martineau ok I guess I'll start a new unit testing, I'm new to unit testing and the assignment was to create a new unit test for our existing project, It was vague, I just need to create a simple unit test in my existing project that will pass when run through the command prompt. Should I create a new file for my unit test and just test it individually? – Fallen Dionysus Mar 04 '21 at 22:40
  • The [Python Module Of The Week](https://pymotw.com/3/) website has some information and examples of how to use the [`unittest`](https://pymotw.com/3/unittest/index.html) module. Note that they are usually run from a command-line shell and the output usually appears there too. Testing what you have in this question should be fairly simple, but testing a Tkinter application could be tricky. – martineau Mar 04 '21 at 22:51
  • @martineau ok is there anything wrong with my unit test function at the top? Also is it possible to test the count word function without testing the tkinter GUI part? My assignment is to only create a unittest that will pass when ran, he didn't specify it had to be the tkinter – Fallen Dionysus Mar 04 '21 at 23:07
  • @martineau let me know if you want me to create a new question for all of this, but could I also create a unit test to test the open txt file function? – Fallen Dionysus Mar 04 '21 at 23:08
  • I think you should post a separate question about how to create and run a unit test. Once that is answered, then you can start working on and asking about those other parts of everything you want to do. – martineau Mar 04 '21 at 23:14
  • @martineau In the early 2000s, the IDLE developers added what is now the normal mode of running user code in a separate non-gui process instead of running user code in the same process as the GUI. (One now has to pass -n on a command line to get the old behavior, and it is somewhat deprecated.) One major reason for this was to avoid interference between user tkinter code and the IDLE GUI. – Terry Jan Reedy Mar 05 '21 at 04:28
  • @TerryJanReedy: Indeed, apparently what I was paraphrasing was an [excerpt](https://flylib.com/books/en/2.725.1.35/1/) from the 2003 book by Mark Lutz titled [Learning Python: Powerful Object-Oriented Programming](https://flylib.com/books/en/2.725.1/) — which is why I hedged what I said a little in later comments. The issue here is that Tkinter applications are inherently incompatible with how `unittest` works plus the unit test itself wasn't coded correctly. The multiple compounding errors make identifying all the issues a fairly difficult task, much less fixing them all. – martineau Mar 05 '21 at 07:53
  • @martineau Yes. The comment I did not add is that I have mixed code and test code in files for a project where that is appropriate, but I would never do that with a tkinter app using mainloop. IDLE's separate test files create `root = tkinter.Tk` and even `root.update` as needed, but never `root.mainloop`. Gracefully shutting down without TclErrors is also a problem at times. Eight years after creating `idlelib/idle_test`, it is only half complete and I am still learning more about testing a tkinter app. – Terry Jan Reedy Mar 06 '21 at 21:39
  • @TerryJanReedy: I modified my non-answer below to show how to run a unit test programmatically and capture the results — which probably could have also been done in a separate thread or process. It occurred to me that one way to do everything within the context of a tkinter application might be something along the lines of what's in the `errorwindow3k.py` module I posted once in [an answer](https://stackoverflow.com/a/49016673/355230) to a question about displaying the output of programs in tkinter GUIs. I wonder it the module would work OK if the application was run from IDLE… – martineau Mar 06 '21 at 22:18

1 Answers1

0

Disclaimer: This does not answer your question. It's merely an example of how to use the unittest module programmatically as opposed to from the command-line and capture its output (although not from IDLE and/or as part of a Tkinter application). However both of these things (running the test from with another it and capturing the results) would be necessary lly in your Tkinter application.)

The code being tested does several things very similar to what you have in your question.

Which is, namely, to count the words in a text file two different ways — there's actually two separate tests involved — one uses a collections.Counter dictionary subclass and the other does to the same thing is done "manually". Afterwards the results of each are compared to the expected results.

Here's the contents the very simple test file used for testing:

Here's one line
And another line
And another line make three

And here's the code:

import collections
from io import StringIO
import unittest


class TestWordCounts(unittest.TestCase):
    TEST_FILENAME = './sample_e_input.txt'
    EXPECTED_RESULT = {"heres": 1,
                       'one': 1,
                       'line': 3,
                       'and': 2,
                       'another': 2,
                       'make': 1,
                       'three': 1}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.get_text()

    def test_collections_counter(self):
        ''' Count words collections.Counter. '''
        counter = collections.Counter((word for word in self.clean_text.split()))
        self.assertEqual(len(counter), len(self.EXPECTED_RESULT))
        self.assertEqual(counter, self.EXPECTED_RESULT)
        print('test_collections_counter passed')

    def test_manual_count(self):
        ''' Count words manually. '''
        word_counts = self.count_words(self.clean_text)
        self.assertEqual(len(word_counts), len(self.EXPECTED_RESULT))
        self.assertEqual(word_counts, self.EXPECTED_RESULT)
        print('test_manual_count passed')

    def get_text(self):
        ''' Read test file then convert to lowercase and remove punctuation. '''
        with open(self.TEST_FILENAME, encoding="utf8") as file:
            text = file.read()

        cleaned = text.lower()
        for substring in '. , : " \' ! *'.split():
            cleaned = cleaned.replace(substring, "")
        self.clean_text = cleaned

    def count_words(self, file_text):
        wordcount = collections.defaultdict(int)
        for word in file_text.split():
            wordcount[word] += 1
        return dict(wordcount)


if __name__ == '__main__':

    # Run unit test, capture output, and then print it.
    output = StringIO()
    tests = unittest.TestLoader().loadTestsFromTestCase(TestWordCounts)
    test_result = unittest.TextTestRunner(stream=output).run(tests)
    print(output.getvalue())  # Print captured output from running test.

Output printed:

test_collections_counter passed
test_manual_count passed
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
martineau
  • 119,623
  • 25
  • 170
  • 301
  • I created a new post as requested of me https://stackoverflow.com/questions/66485721/how-to-make-and-run-a-unit-test-in-python-idle also do you think you could explain why my unit test would be invalid and teach me how to fix it up or create my own? – Fallen Dionysus Mar 05 '21 at 02:04