08-19-2019 12:19 PM - edited 08-19-2019 12:22 PM
Hi all,
is it possible to mockup the arguments (tctx, root, service, proplist) for the service callback without using of NCS daemon?
The approch should be to validate the output based on the input for simple unit testing with pyTest and speed up the development to avoid package reload after every code change.
For example: Inside cb_create does exist a function to calculate broadcast ip address based on passed ip address. I want to validate the calculated ip address.
import ncs from ncs.application import Service # ------------------------ # SERVICE CALLBACK EXAMPLE # ------------------------ class ServiceCallbacks(Service): # The create() callback is invoked inside NCS FASTMAP and # must always exist. @Service.create def cb_create(self, tctx, root, service, proplist): self.log.info('Service create(service=', service._path, ')')
I am aware of it, that the tests need further stages inside ncs to validate the service.
Thanks.
Solved! Go to Solution.
08-19-2019 11:36 PM
I'm not the unit tests expert (sadly), but asked myself the same question a while back, and was able to come up with something.
Probably there are more elegant ways to do that...
The tests below are testing a very simple NSO service package. It tests the methods that the create callback is calling and then also tests cb_create() itself to make sure it calls everything it should.
Hope it helps!
#!/usr/bin/env python # -*- coding: utf-8 -*- import mock import unittest import sys sys.path.insert(0, '../../python') mock.patch('ncs.application.Service.create', staticmethod(lambda x: x)).start() from nso_basic.main import ServiceCallbacks # noqa: E402 @mock.patch.object(ServiceCallbacks, "__init__", lambda x, y, z, w: None) class nsoBasicTestCase(unittest.TestCase): def test_foo(self): # instantiate our service instance = ServiceCallbacks(None, None, None) instance.log = log() self.assertEqual(instance.foo(None), None, "Should be None") def test_calc_mask(self): # instantiate our service instance = ServiceCallbacks(None, None, None) instance.log = log() service = mock.Mock() service.ip = '1.2.3.4' service.mask = '16' self.assertEqual(instance.calc_mask(service), '255.255.0.0', 'Should be 255.255.0.0') def test_calc_desc(self): # instantiate our service instance = ServiceCallbacks(None, None, None) instance.log = log() service = mock.Mock() service.description = 'short desc' self.assertEqual(instance.calc_desc(service), 'short desc', 'Should be short desc') service.description = 'very very long description' self.assertEqual(instance.calc_desc(service), 'very-ption', 'Should be very-ption') # decorators are applied BOTTOM-UP. # Order of argument for the test method should match @mock.patch('ncs.template.Template', autospec=True) @mock.patch('ncs.template.Variables', autospec=True) def test_cb_create(self, mock_variable, mock_template): ''' Test that cb_create is calling all functions it suppose to call. ''' # instantiate our service instance = ServiceCallbacks(None, None, None) instance.log = log() instance.calc_mask = mock.MagicMock(return_value='255.255.0.0') instance.calc_desc = mock.MagicMock(return_value='short desc') service = mock.Mock() service._path = 'cb/create/test' service.description = 'short desc' service.ip = '1.2.3.4' service.mask = '16' # Mock a Variables instance to be able to count the times add is called instance.mock_var = mock_variable() # Same for Template and apply method instance.mock_temp = mock_template(service) tctx = None root = None proplist = None instance.cb_create(tctx, root, service, proplist) self.assertTrue(instance.calc_mask.called) self.assertTrue(instance.calc_desc.called) self.assertEqual(instance.mock_var.add.call_count, 2) self.assertEqual(instance.mock_temp.apply.call_count, 1) class log(): def debug(self, *args): print('DEBUG: {}'.format(''.join(str(x) for x in args))) def info(self, *args): print('INFO: {}'.format(''.join(str(x) for x in args))) def error(self, *args): print('ERROR: {}'.format(''.join(str(x) for x in args)))
08-19-2019 11:36 PM
I'm not the unit tests expert (sadly), but asked myself the same question a while back, and was able to come up with something.
Probably there are more elegant ways to do that...
The tests below are testing a very simple NSO service package. It tests the methods that the create callback is calling and then also tests cb_create() itself to make sure it calls everything it should.
Hope it helps!
#!/usr/bin/env python # -*- coding: utf-8 -*- import mock import unittest import sys sys.path.insert(0, '../../python') mock.patch('ncs.application.Service.create', staticmethod(lambda x: x)).start() from nso_basic.main import ServiceCallbacks # noqa: E402 @mock.patch.object(ServiceCallbacks, "__init__", lambda x, y, z, w: None) class nsoBasicTestCase(unittest.TestCase): def test_foo(self): # instantiate our service instance = ServiceCallbacks(None, None, None) instance.log = log() self.assertEqual(instance.foo(None), None, "Should be None") def test_calc_mask(self): # instantiate our service instance = ServiceCallbacks(None, None, None) instance.log = log() service = mock.Mock() service.ip = '1.2.3.4' service.mask = '16' self.assertEqual(instance.calc_mask(service), '255.255.0.0', 'Should be 255.255.0.0') def test_calc_desc(self): # instantiate our service instance = ServiceCallbacks(None, None, None) instance.log = log() service = mock.Mock() service.description = 'short desc' self.assertEqual(instance.calc_desc(service), 'short desc', 'Should be short desc') service.description = 'very very long description' self.assertEqual(instance.calc_desc(service), 'very-ption', 'Should be very-ption') # decorators are applied BOTTOM-UP. # Order of argument for the test method should match @mock.patch('ncs.template.Template', autospec=True) @mock.patch('ncs.template.Variables', autospec=True) def test_cb_create(self, mock_variable, mock_template): ''' Test that cb_create is calling all functions it suppose to call. ''' # instantiate our service instance = ServiceCallbacks(None, None, None) instance.log = log() instance.calc_mask = mock.MagicMock(return_value='255.255.0.0') instance.calc_desc = mock.MagicMock(return_value='short desc') service = mock.Mock() service._path = 'cb/create/test' service.description = 'short desc' service.ip = '1.2.3.4' service.mask = '16' # Mock a Variables instance to be able to count the times add is called instance.mock_var = mock_variable() # Same for Template and apply method instance.mock_temp = mock_template(service) tctx = None root = None proplist = None instance.cb_create(tctx, root, service, proplist) self.assertTrue(instance.calc_mask.called) self.assertTrue(instance.calc_desc.called) self.assertEqual(instance.mock_var.add.call_count, 2) self.assertEqual(instance.mock_temp.apply.call_count, 1) class log(): def debug(self, *args): print('DEBUG: {}'.format(''.join(str(x) for x in args))) def info(self, *args): print('INFO: {}'.format(''.join(str(x) for x in args))) def error(self, *args): print('ERROR: {}'.format(''.join(str(x) for x in args)))
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide