diff --git a/setup.py b/setup.py
index bac7c64cc06f5e41706aca058791bf95188db174..d63c0e78e895b0cd7aa018ac1f27becbd1fed7c4 100755
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ setup(
     long_description=open('README.md').read(),
     long_description_content_type='text/markdown',
     url='https://gitlab.developers.cam.ac.uk/uis/devops/django/ucamlookup',
-    version='3.0.0',
+    version='3.0.2',
     license='MIT',
     author='DevOps Division, University Information Services, University of Cambridge',
     author_email='devops@uis.cam.ac.uk',
diff --git a/tox.ini b/tox.ini
index 1e75cd30a57b16dc2fc7566c607d703e8d5ad3d8..9c18e2b01ac02bf4cd83baba6a89d70961a274c4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -36,6 +36,7 @@ setenv=
     COVERAGE_FILE={env:TOXINI_COVERAGE_FILE:{toxinidir}/.coverage}
 # Additional dependencies
 deps=
+    mock
     coverage
     django111: Django>=1.11,<2.0
     django20: Django>=2.0,<2.1
@@ -52,6 +53,7 @@ deps=
 #   We specify a specific version of flake8 to avoid introducing "false"
 #   regressions when new checks are introduced. The version of flake8 used may
 #   be overridden via the TOXINI_FLAKE8_VERSION environment variable.
+    mock
     flake8=={env:TOXINI_FLAKE8_VERSION:3.6.0}
 commands=
     flake8 --version
@@ -61,6 +63,7 @@ commands=
 [testenv:coverage]
 basepython=python3
 deps=
+    mock
     coverage
     django
 # Specify the default environment. Note that tox will *always* use the testsuite
diff --git a/ucamlookup/tests.py b/ucamlookup/tests.py
index 29ecb4dc7119d5a49af1be13706d8c9d40b5bc65..8c1ec6e6c71c9bb1af64c9bfdf85ec3b9d12a6a8 100644
--- a/ucamlookup/tests.py
+++ b/ucamlookup/tests.py
@@ -2,23 +2,25 @@ import json
 import sys
 from django.contrib.auth.models import User
 from django.core.exceptions import ValidationError
+from mock import patch, Mock
+
 try:
     from django.core.urlresolvers import reverse
-except Exception:
+except ImportError:
     from django.urls import reverse
 from django.test import TestCase, override_settings
 from ucamlookup.models import LookupGroup
 from ucamlookup.utils import user_in_groups, get_users_from_query, return_visibleName_by_crsid, get_groups_from_query, \
     return_title_by_groupid, get_group_ids_of_a_user_in_lookup, get_institutions, get_institution_name_by_id, \
-    validate_crsid_list, validate_groupid_list, get_connection
+    validate_crsid_list, validate_groupid_list, get_connection, get_user_lookupgroups, get_users_of_a_group
 
 
-class UcamLookupTests(TestCase):
+class UcamLookupOptionsTests(TestCase):
 
     @override_settings(UCAMLOOKUP_HOST="mock_host", UCAMLOOKUP_PORT=80, UCAMLOOKUP_URL_BASE="/mock",
                        UCAMLOOKUP_CHECK_CERTS=False, UCAMLOOKUP_USERNAME="mock_username",
                        UCAMLOOKUP_PASSWORD="mock_password")
-    def test_optional_settins(self):
+    def test_optional_settings(self):
             conn = get_connection()
             self.assertEqual(conn.host, "mock_host")
             self.assertEqual(conn.port, 80)
@@ -27,6 +29,78 @@ class UcamLookupTests(TestCase):
             self.assertEqual(conn.username, "mock_username")
             self.assertEqual(conn.password, "mock_password")
 
+
+class UcamLookupTests(TestCase):
+
+    def setUp(self):
+        # fixture for group 101888
+        self.mock_101888 = Mock(groupid='101888', title='CS Information Systems team')
+        # fixture for institution UIS
+        mock_uis = Mock(instid='UIS')
+        mock_uis.name = 'University Information Services'
+        # fixture for institution CL
+        mock_cl = Mock(instid='CL')
+        mock_cl.name = 'Department of Computer Science and Technology'
+
+        # patch the lookup get_connection()
+        self.patcher = patch('ucamlookup.utils.get_connection')
+        mock_get_connection = self.patcher.start()
+
+        # a mock result returned by invoke_method()
+        mock_result = Mock(error=None)
+
+        def side_effect(_, path, path_params, __, ___):
+            """
+            Side effect method that mocks the lookup connection's invoke_method() and returns a
+            mock result.
+            """
+            try:
+                path = path % path_params
+            except KeyError:
+                pass
+
+            if path == 'api/v1/person/crsid/amc203':
+                mock_result.person.visibleName = 'Dr Abraham Martin'
+            elif path == 'api/v1/person/crsid/jw35':
+                mock_result.person.visibleName = 'John Warbrick'
+            elif path == 'api/v1/person/crsid/test0001':
+                mock_result.person.visibleName = 'Test User 1'
+            elif path == 'api/v1/person/crsid/amc20311':
+                mock_result.person = None
+            elif path == 'api/v1/group/101888':
+                mock_result.group.title = 'CS Information Systems team'
+            elif path == 'api/v1/group/101923':
+                mock_result.group.title = 'UIS Finance team'
+            elif path == 'api/v1/group/203840928304982':
+                mock_result.group = None
+            elif path == 'api/v1/person/search':
+                mock_result.people = [
+                    Mock(visibleName='Dr Abraham Martin', **{'identifier.value': 'amc203'})
+                ]
+            elif path == 'api/v1/person/crsid/test0001/insts':
+                mock_result.institutions = []
+            elif path == 'api/v1/person/crsid/amc203/insts':
+                mock_result.institutions = [mock_cl]
+            elif path == 'api/v1/inst/all-insts':
+                mock_result.institutions = [mock_uis, mock_cl]
+            elif path == 'api/v1/inst/UIS':
+                mock_result.institution.name = mock_uis.name
+            elif path == 'api/v1/group/search':
+                mock_result.groups = [self.mock_101888]
+            elif path == 'api/v1/person/crsid/amc203/groups':
+                mock_result.groups = [self.mock_101888]
+            elif path == 'api/v1/group/101888/members':
+                mock_result.people = [Mock(visibleName='Dr Abraham Martin', identifier='amc203')]
+            else:
+                self.fail("%s hasn't been mocked" % path)
+            return mock_result
+
+        # mock connection returned by get_connectgion()
+        mock_connection = Mock()
+        mock_connection.invoke_method.side_effect = side_effect
+
+        mock_get_connection.return_value = mock_connection
+
     def test_add_name_to_user_and_add_title_to_group(self):
         with self.assertRaises(User.DoesNotExist):
             User.objects.get(username="amc203")
@@ -93,8 +167,15 @@ class UcamLookupTests(TestCase):
     def test_get_institutions_with_user(self):
         amc203 = User.objects.create_user(username="amc203")
         results = get_institutions(user=amc203)
+        self.assertEquals(("CL", "Department of Computer Science and Technology"), results[0])
         self.assertIn(("UIS", "University Information Services"), results)
 
+    def test_get_institutions_with_non_existant_user(self):
+        all_institutions = get_institutions()
+        test0001 = User.objects.create_user(username="test0001")
+        results = get_institutions(user=test0001)
+        self.assertEqual(all_institutions, results)
+
     def test_get_institution_name_by_id(self):
         result = get_institution_name_by_id(institution_id="UIS")
         self.assertEqual("University Information Services", result)
@@ -104,11 +185,6 @@ class UcamLookupTests(TestCase):
         result = get_institution_name_by_id(institution_id="UIS", all_institutions=all_institutions)
         self.assertEqual("University Information Services", result)
 
-        test_user = User.objects.create_user(username="test0001")
-        results = get_institutions(user=test_user)
-
-        self.assertEqual(all_institutions, results)
-
     def test_views_without_login(self):
         response = self.client.get(reverse('ucamlookup_find_people'), {'query': 'amc203', 'searchId_u': '1'})
         self.assertEqual(response.status_code, 302)
@@ -190,3 +266,19 @@ class UcamLookupTests(TestCase):
 
         with self.assertRaises(ValidationError):
             validate_groupid_list(["kaskvdkam20e9mciasmdimadf"])
+
+    def test_get_user_lookupgroups(self):
+        amc203 = User.objects.create_user(username="amc203")
+        groups = get_user_lookupgroups(amc203)
+        # check
+        group = next(group for group in groups if group.lookup_id == '101888')
+        self.assertEqual(group.name, 'CS Information Systems team')
+
+    def test_get_users_of_a_group(self):
+        users = get_users_of_a_group(self.mock_101888)
+        # check
+        user = next(user for user in users if user.username == 'amc203')
+        self.assertEqual(user.last_name, 'Dr Abraham Martin')
+
+    def tearDown(self):
+        self.patcher.stop()
diff --git a/ucamlookup/utils.py b/ucamlookup/utils.py
index 45867399fe6a6cc484ee24f9fb691ec2f39a34fd..2f20b28ff9dd92dfc7e5b839ea82157decc3d07b 100644
--- a/ucamlookup/utils.py
+++ b/ucamlookup/utils.py
@@ -50,7 +50,6 @@ def get_groups_from_query(search_string):
 
 def return_title_by_groupid(groupid):
     group = GroupMethods(get_connection()).getGroup(groupid=groupid)
-    # TODO If a group does not exists in lookup should we allow it?
     if group is None:
         raise ValidationError("The group with id %(groupid)s does not exist in Lookup", code='invalid',
                               params={'groupid': groupid},)
@@ -103,7 +102,7 @@ def get_institutions(user=None):
 
     all_institutions = InstitutionMethods(get_connection()).allInsts(includeCancelled=False)
     # filter all the institutions that were created for store year students
-    all_institutions = list(filter(lambda institution: re.match(r'.*\d{2}$', institution.id) is None,
+    all_institutions = list(filter(lambda institution: re.match(r'.*\d{2}$', institution.instid) is None,
                                    all_institutions))
 
     if user is not None: