Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python unittesting for a class method

A beginner level question.. trying to understand how I can best use the built-in unittest. In the trivial example below, the method consume_food picks a food item and then I am calling food.cut() method. In future, this method may return instance of Drink object. The #commented code indicates one possible future implementation. In this case, self.milk will not have the cut method defined.

I want to add a unit test for consume_food and pick_food methods. I would like to do this for the original implementation first and then change it after adding self.milk functionality.

EDIT: The intention is to write a unit test for an existing api, so that I can capture any such changes ( i.e. absence of Drink.cut method) forcing me to update the methods and unit tests.

Can someone please help showing me how to write a unit test for this example?

class Fruit:
    def cut(self):
        print("cut the fruit")

class Drink:
    def pour(self):
       print("pour the drink")


class A:
   def __init__(self):
       self.apple = Fruit()
       self.banana=Fruit()
       #self.milk = Drink()
       #self.liquid_diet = True

   def consume_food(self):
       food = pick_food()
       food.cut()
       print("consuming the food")

   def pick_food(self):
       return self.apple                                 
       #if self.liquid_diet: return self.milk
       #return self.apple
like image 469
stackjs Avatar asked Sep 19 '15 19:09

stackjs


People also ask

How do you test a class method in Python?

First you need to create a test file. Then import the unittest module, define the testing class that inherits from unittest. TestCase, and lastly, write a series of methods to test all the cases of your function's behavior. First, you need to import a unittest and the function you want to test, formatted_name() .

How do you unit test methods?

A typical unit test contains 3 phases: First, it initializes a small piece of an application it wants to test (also known as the system under test, or SUT), then it applies some stimulus to the system under test (usually by calling a method on it), and finally, it observes the resulting behavior.

Can you use a main () method for unit testing?

It is possible to write a main() method in each class that need to be tested for unit testing. In the main() method, you could create test object of the class itself, and write some tests to test its methods.


1 Answers

The thing is, your cut() and consume_food() methods don't really do much right now that allow you to make meaningful assertions after you execute them in a test.

So I'd suggest to expand your initial code a little bit to have those methods act upon the respective objects so that you can make meaningful assertions on their state after invoking those methods.

Right now, all they really do is write to STDOUT, which is sort of a global state - which should generally be avoided and is always difficult to test. (I'm not saying that printing output is a bad thing - but if that's the only thing your code does, it's going to be very tricky to test).

So I introduced a common superclass Food which has a consume() method, and sets a corresponding attribute. Similarly, the cut() method on Fruit now sets an attribute that you can test for.

import unittest


class Food(object):
    def __init__(self):
        self.consumed = False

    def consume(self):
        self.consumed = True


class Fruit(Food):
    def __init__(self):
        super(Fruit, self).__init__()
        self.been_cut = False

    def cut(self):
        print("cut the fruit")
        self.been_cut = True


class Consumer(object):
    def __init__(self):
        self.apple = Fruit()
        self.banana = Fruit()

    def consume_food(self):
        food = self.pick_food()
        food.cut()
        print("consuming the food")
        food.consume()

    def pick_food(self):
        return self.apple

These tests now can make assertions on the object's states after the relevant methods have been invoked. Note that they follow the AAA pattern - Arrange Act Assert:

  • First, you arrange the objects under test the way you need them (instantiate a consumer).
  • Then you act on the objects under test (invoking the method in question)
  • Finally, you make assertions on the resulting state you expect the objects to be in
class TestConsumer(unittest.TestCase):

    def test_consume_food_consumes_the_apple(self):
        c = Consumer()
        c.consume_food()
        self.assertTrue(c.apple.consumed,
                        "Expected apple to be consumed")

    def test_consume_food_cuts_the_food(self):
        c = Consumer()
        c.consume_food()
        self.assertTrue(c.apple.been_cut,
                        "Expected apple to be cut")

    def test_pick_food_always_selects_the_apple(self):
        c = Consumer()
        food = c.pick_food()
        self.assertEquals(c.apple, food,
                          "Expected apple to have been picked")


if __name__ == '__main__':
    unittest.main()
like image 151
Lukas Graf Avatar answered Sep 20 '22 13:09

Lukas Graf