0

I have a list CountryList[] defined in my function and I want to check that it is not empty. I initialize it as empty but later on in the function data is put into it. This is the unit test I have typed.

 def assertEmpty(self, CountryList):
      self.assertFalse(CountryList)

 def assertNotEmpty(self, CountryList):
      self.assertTrue(CountryList)

This is the method in my program.

def onCountry(self, doc_id):
        if(doc_id==None):
            return

        output_list = self.findBySubjectDocId(doc_id)

        country_list=[]
        for x in output_list:
            country_id=x["visitor_country"]
            if(SHOW_FULL_NAMES):
                country_id=pc.country_alpha2_to_country_name(country_id)
            country_list.append(country_id)

        ts=pd.Series(country_list).value_counts().plot(kind='bar',color='purple')
        plt.xticks(rotation='horizontal')
        plt.xlabel('Country')
        plt.ylabel('Number of Viewers')
        plt.title("Viewers based on Country")
        ts.plot()
        plt.show()

        print("Countries of Visitors:")
        x = []
        y = []
        for k,v in Counter(country_list).items():
            x.append(k)
            y.append(v)
            print(k,"-",v)

Do you suggest I test out this code some other way? or is the above testing acceptable?

abc
  • 39
  • 6
  • Where do you create this `CountryList`? Is it created in the tests somehow or in your code? Are your `assertEmpty` and `assertNotEmpty` functions really global or are they in a class? – Code-Apprentice Dec 06 '21 at 16:30
  • @Code-Apprentice CountryList is in a .py file called program and assertEmpty and asertNotEmpty are in a another Test_Browser.py file – abc Dec 06 '21 at 16:36
  • Please [edit] your code to explain the file structure. For more tips on creating an example code snippet, read [mcve]. – Code-Apprentice Dec 07 '21 at 17:22

2 Answers2

0

Here are several suggestions:

  1. To properly use Python's unittest package, you need to create a class which extends unittest.TestCase.

  2. Name your test methods test_* rather than assert*.

  3. Tests can only take one argument self.

  4. You can define a setUp() method which runs before each test_* method. Use this to create data that is common to all tests.

  5. Tests are code just like any other code and follow all the same rules.

  6. First describe in words the scenario you are trying to test. Often this is of the form "When I call function F with parameters P, then the result will be R". Then write the test to replicate this specific scenario.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • Thank you for this reply but I am confused on what I actually need to test here since this method is for plotting a histogram. I want to call the function onCountry(self, doc_id) by passng a document id in the place of doc_ id but I do not know how to check if it plots the histogram according to the data. – abc Dec 06 '21 at 16:39
  • @abc Is this a homework assignment? I mean where do the requirements for testing come from? As I explain in step 6, unit tests should test the behavior of a function. By behavior, I mean that the test should clearly show that for certain input the function gives the correct output. Maybe you need to split up your `onCountry()` function into smaller helper functions. Then you can test each of those individually. – Code-Apprentice Dec 07 '21 at 17:25
0

Since country_list is a local variable that isn't returned, there's not an easy way for your unit test to check it directly; your test helpers seem fine, but you don't seem to have any actual testing that uses them, because the code has been written in a way that's difficult to test.

One way to make this easier to test would be to refactor the function into two pieces like this:

def build_country_list(self, doc_id):
    output_list = self.findBySubjectDocId(doc_id)

    country_list=[]
    for x in output_list:
        country_id=x["visitor_country"]
        if(SHOW_FULL_NAMES):
            country_id=pc.country_alpha2_to_country_name(country_id)
        country_list.append(country_id)
    return country_list

def onCountry(self, doc_id):
    if doc_id is None:
        return
    country_list = self.build_country_list(doc_id)
        
    ts=pd.Series(country_list).value_counts().plot(kind='bar',color='purple')
    plt.xticks(rotation='horizontal')
    plt.xlabel('Country')
    plt.ylabel('Number of Viewers')
    plt.title("Viewers based on Country")
    ts.plot()
    plt.show()

    print("Countries of Visitors:")
    x = []
    y = []
    for k,v in Counter(country_list).items():
        x.append(k)
        y.append(v)
        print(k,"-",v)

Now you can write a unit test for build_country_list that verifies that your logic of building the list is correct, separately from the onCountry logic that also plots and prints the data, something like:

def test_build_country_list(self):
    # probably need to have done some setUp that sets up a test_doc_id?
    self.assertNotEmpty(self.test_instance.build_country_list(self.test_doc_id))

If you want to also test the output logic, you'll need to mock out the output functions and write a test that verifies that they get called with the correct arguments when onCountry is called.

Samwise
  • 68,105
  • 3
  • 30
  • 44
  • Hi, Thank you for the response. What did you mean by probably need to have done some setUp that sets up a test_doc_id? – abc Dec 06 '21 at 16:51
  • You need to set up an instance of your class for testing purposes such that `findBySubjectDocId(test_doc_id)` will produce something useful. The standard place to do this is in the `setUp()` method of a unit test. I can't see the rest of your class under test so I can't offer any specific pointers on how you'd set it up with useful test data. – Samwise Dec 06 '21 at 16:54
  • An alternative method would be to `patch` out `findBySubjectDocId` -- but I'd assume that function is something you'd want test coverage on as well. – Samwise Dec 06 '21 at 16:56