1

If you have seen my other question you will know I am having a very hard time at the moment with unit tests in Python. Two days of trying and I've made no progress.

In my method which is part of a Class there is several calls to a DAL.

car_registration = self.dal.cars.get_by_registration('121D121')

This DAL is configured in the base class. I want to completely overrid/mock these calls when running my unit tests and instead return predefined responses so I can continue with the method and ensure everything works as expected.

The method starts of with:

def change_registration(self):
 body = json.loads(self.request.body)
 registration = body['registration']
 car = self.dal.cars.get_by_registration(registration)

My Python test file at the moment is:

class CarTestCase(unittest.TestCase):
 def setUp(self):
 self.car_controller = CarController()
 def test_change_registrations(self):
 self.car_controller.dal.cars.get_by_registration = MagicMock(return_value=3)
 response = self.car_controller.change_registration()

I am expecting to get the response 3. However, an error is being thrown.

AttributeError: 'CarController' object has no attribute '_py_object'

It appears the mocking isn't working and it still trying to use the main DAL which isn't fully set up when using the unit tests. How do I prevent it for looking for the actual DAL but instead mocks?

asked Nov 7, 2017 at 14:39
2
  • I assume you need to mock the dal attribute itself. Something like self.car_controller.dal = Mock(). Then self.car_controller.dal.cars.get_by_registration.return_value = 3. Commented Nov 7, 2017 at 14:52
  • If I change that line I get the error AttributeError: can't set attribute Commented Nov 7, 2017 at 15:05

3 Answers 3

1

I think you are not showing us the code that triggers the error because there is nothing wrong with your strategy. Using some imagination to mimic code we don't have I can write this and it runs with no problem:

import unittest
from unittest.mock import MagicMock
class CarList():
 def get_by_registration(self, registration):
 pass
class Dal:
 def __init__(self):
 self.cars = CarList() 
 pass
class CarController:
 def __init__(self):
 self.dal = Dal()
 def change_registration(self):
 registration = None
 car = self.dal.cars.get_by_registration(registration)
 return car 
class CarTestCase(unittest.TestCase):
 def setUp(self):
 self.car_controller = CarController()
 def test_change_registrations(self):
 self.car_controller.dal.cars.get_by_registration =\
 MagicMock(return_value=3)
 result = self.car_controller.change_registration()
 self.assertEqual(result, 3)
unittest.main()
answered Nov 7, 2017 at 15:26
Sign up to request clarification or add additional context in comments.

Comments

0

Here my example:

# test_tool_file.py
import unittest
from unittest.mock import patch, Mock, call
import test_tools_file
class MyObject():
 def __init__(self, data):
 self.data = data
 def get_data(self):
 return self.data
 def count(self):
 return len(self.get_data())
class TestFile(unittest.TestCase):
 """ Cas de tests.
 """
 @patch("test_tools_file.MyObject.get_data")
 def test_1(self, mock_get):
 """ test_1
 """
 mock_get.return_value = [1,2,3,4,5,6]
 obj = MyObject(["12", "13"])
 result = obj.count()
 self.assertEqual(result, 6)
answered Nov 7, 2017 at 15:45

Comments

0

Personal opinion: I would suggest you to start simple. Use anything 'magic' once you understand what it provides you over the non-magic way. Using a non-magic solution tends to be easier to understand.

I think you have multiple simple solutions at hand. For what you tried to achieve, instead of:

self.car_controller.dal.cars.get_by_registration = MagicMock(return_value=3)

You could try:

self.car_controller.dal.cars.get_by_registration = lambda: 3

But you mentioned you want to replace all methods. In fact I would consider a 'simple' dependency injection. If you find it difficult to test, it might be a sign that the another design would be better (that's the idea of TDD - Test Driven Design). A simple dependency injection is one where you for example just pass dal to the constructor of CarController.

Here is a complete example with some variations of the test:

from unittest import TestCase
from unittest.mock import Mock
class SomeDal(object):
 def get_something(self):
 return 'something'
class SomeController(object):
 def __init__(self, dal):
 self.dal = dal
 def upper_something(self):
 return self.dal.get_something().upper()
class TestSomeController(TestCase):
 def test_using_simple_patch(self):
 controller = SomeController(SomeDal())
 controller.dal.get_something = lambda: 'anything'
 assert controller.upper_something() == 'ANYTHING'
 def test_using_simple_patch_and_injection(self):
 dal = SomeDal()
 dal.get_something = lambda: 'anything'
 controller = SomeController(dal)
 assert controller.upper_something() == 'ANYTHING'
 def test_using_simple_mock_class(self):
 class MockDal(object):
 def get_something(self):
 return 'anything'
 controller = SomeController(MockDal())
 assert controller.upper_something() == 'ANYTHING'
 def test_using_semi_magic_mock(self):
 mock_dal = Mock(spec=SomeDal)
 mock_dal.get_something.return_value = 'anything'
 controller = SomeController(mock_dal)
 assert controller.upper_something() == 'ANYTHING'
answered Nov 7, 2017 at 15:10

3 Comments

The only reason I used MagicMock was because I came across an example online. I'm still very confused as to how I should be doing this. I'm not sure if I should have self. because its a member of the CarController Class and not the test Class for example.
Yes, there are a lot of examples in the internet. I've added some simple examples to give you some ideas. BTW I prefer pytest over unittest - no need for inheritance. But it's a minor detail. I'm not saying not to use Mock or MagicMock. But use it when you understand why you should use them.
if you use self inside the TestCase class it can look as you are mocking the TestCase and not CarController, but as you write self.car_controller... this is a reference to a CarController object so no problem.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.