Merge lp:~billy-olsen/charms/trusty/mongodb/sample-unittest into lp:~mariosplivalo/charms/trusty/mongodb/replsets-fix-try

Proposed by Billy Olsen
Status: Merged
Merged at revision: 82
Proposed branch: lp:~billy-olsen/charms/trusty/mongodb/sample-unittest
Merge into: lp:~mariosplivalo/charms/trusty/mongodb/replsets-fix-try
Diff against target: 207 lines (+188/-0)
4 files modified
setup.cfg (+6/-0)
unit_tests/__init__.py (+2/-0)
unit_tests/test_hooks.py (+69/-0)
unit_tests/test_utils.py (+111/-0)
To merge this branch: bzr merge lp:~billy-olsen/charms/trusty/mongodb/sample-unittest
Reviewer Review Type Date Requested Status
Mario Splivalo Approve
Review via email: mp+244661@code.launchpad.net

Description of the change

Sample test case for testing one branch of the replicaset-relation-joined hook.

Includes comments for clarity (hopefully).

To post a comment you must log in.
Revision history for this message
Mario Splivalo (mariosplivalo) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'setup.cfg'
--- setup.cfg 1970-01-01 00:00:00 +0000
+++ setup.cfg 2014-12-13 00:22:04 +0000
@@ -0,0 +1,6 @@
1[nosetests]
2verbosity=2
3with-coverage=1
4cover-erase=1
5cover-package=hooks
6
07
=== added file 'unit_tests/__init__.py'
--- unit_tests/__init__.py 1970-01-01 00:00:00 +0000
+++ unit_tests/__init__.py 2014-12-13 00:22:04 +0000
@@ -0,0 +1,2 @@
1import sys
2sys.path.append('hooks')
03
=== added file 'unit_tests/test_hooks.py'
--- unit_tests/test_hooks.py 1970-01-01 00:00:00 +0000
+++ unit_tests/test_hooks.py 2014-12-13 00:22:04 +0000
@@ -0,0 +1,69 @@
1from mock import patch
2
3import hooks
4
5from test_utils import CharmTestCase
6
7# Defines a set of functions to patch on the hooks object. Any of these
8# methods will be patched by default on the default invocations of the
9# hooks.some_func(). Invoking the the interface change relations will cause
10# the hooks context to be created outside of the normal mockery.
11TO_PATCH = [
12 'relation_id',
13 'relation_get',
14 'relation_set',
15 'unit_get',
16 'juju_log',
17 'config',
18]
19
20
21class MongoHooksTest(CharmTestCase):
22
23 def setUp(self):
24 super(MongoHooksTest, self).setUp(hooks, TO_PATCH)
25
26 # The self.config object can be used for direct invocations of the
27 # hooks methods. The side_effect of invoking the config object within
28 # the hooks object will return the value that is set in the test case's
29 # test_config dictionary
30 self.config.side_effect = self.test_config.get
31
32 # Note: if we need to mock a specific class of an object, we need to
33 # invoke the patch.object rather than simply mocking the module itself.
34 # This is typically recommended for patching any object which belongs
35 # to the object under test (lookup partial-mocks for more information).
36 @patch.object(hooks, 'restart_mongod')
37 @patch.object(hooks, 'enable_replset')
38 # Note: patching the os.environ dictionary in-line here so there's no
39 # additional parameter sent into the function
40 @patch.dict('os.environ', JUJU_UNIT_NAME='fake-unit/0')
41 def test_replica_set_relation_joined(self, mock_enable, mock_restart):
42 # only have 1 invocation of the unit_get, so we'll just tell it what
43 # to return. Can change to be more sophisticated when necessary.
44 self.unit_get.return_value = 'private.address'
45 self.test_config.set('port', '1234')
46 self.test_config.set('replicaset', 'fake-replicaset')
47 self.relation_id.return_value = 'fake-relation-id'
48
49 # Partial mock to control the flow around the if check for the enable
50 # restart.
51 mock_enable.return_value = False
52
53 # This tests the trigger of firing the relation-joined as the python
54 # invocation works. However, it doesn't seem to accept all the mocking
55 # in this case, so I'll just invoke the function directly.
56 # hooks.hooks.execute(['hooks/replicaset-relation-joined'])
57 hooks.replica_set_relation_joined()
58
59 # Verify that mongodb was NOT restarted since the replicaset we claimed
60 # was not enabled.
61 self.assertFalse(mock_restart.called)
62
63 exp_rel_vals = {'hostname': 'private.address',
64 'port': '1234',
65 'replset': 'fake-replicaset',
66 'install-order': '0',
67 'type': 'replset'}
68 # Check that the relation data was set as we expect it to be set.
69 self.relation_set.assert_called_with('fake-relation-id', exp_rel_vals)
070
=== added file 'unit_tests/test_utils.py'
--- unit_tests/test_utils.py 1970-01-01 00:00:00 +0000
+++ unit_tests/test_utils.py 2014-12-13 00:22:04 +0000
@@ -0,0 +1,111 @@
1import logging
2import unittest
3import os
4import yaml
5import io
6
7from contextlib import contextmanager
8from mock import patch
9
10
11@contextmanager
12def mock_open(filename, contents=None):
13 ''' Slightly simpler mock of open to return contents for filename '''
14 def mock_file(*args):
15 if args[0] == filename:
16 return io.StringIO(contents)
17 else:
18 return open(*args)
19 with patch('__builtin__.open', mock_file):
20 yield
21
22
23def load_config():
24 '''
25 Walk backwords from __file__ looking for config.yaml, load and return the
26 'options' section'
27 '''
28 config = None
29 f = __file__
30 while config is None:
31 d = os.path.dirname(f)
32 if os.path.isfile(os.path.join(d, 'config.yaml')):
33 config = os.path.join(d, 'config.yaml')
34 break
35 f = d
36
37 if not config:
38 logging.error('Could not find config.yaml in any parent directory '
39 'of %s. ' % file)
40 raise Exception
41
42 return yaml.safe_load(open(config).read())['options']
43
44
45def get_default_config():
46 '''
47 Load default charm config from config.yaml return as a dict.
48 If no default is set in config.yaml, its value is None.
49 '''
50 default_config = {}
51 config = load_config()
52 for k, v in config.iteritems():
53 if 'default' in v:
54 default_config[k] = v['default']
55 else:
56 default_config[k] = None
57 return default_config
58
59
60class CharmTestCase(unittest.TestCase):
61 def setUp(self, obj, patches):
62 super(CharmTestCase, self).setUp()
63 self.patches = patches
64 self.obj = obj
65 self.test_config = TestConfig()
66 self.test_relation = TestRelation()
67 self.patch_all()
68
69 def patch(self, method):
70 _m = patch.object(self.obj, method)
71 mock = _m.start()
72 self.addCleanup(_m.stop)
73 return mock
74
75 def patch_all(self):
76 for method in self.patches:
77 setattr(self, method, self.patch(method))
78
79
80class TestConfig(object):
81 def __init__(self):
82 self.config = get_default_config()
83
84 def get(self, attr):
85 try:
86 return self.config[attr]
87 except KeyError:
88 return None
89
90 def get_all(self):
91 return self.config
92
93 def set(self, attr, value):
94 if attr not in self.config:
95 raise KeyError
96 self.config[attr] = value
97
98
99class TestRelation(object):
100 def __init__(self, relation_data={}):
101 self.relation_data = relation_data
102
103 def set(self, relation_data):
104 self.relation_data = relation_data
105
106 def get(self, attr=None, unit=None, rid=None):
107 if attr is None:
108 return self.relation_data
109 elif attr in self.relation_data:
110 return self.relation_data[attr]
111 return None

Subscribers

People subscribed via source and target branches

to all changes: