8000 feat: added unit test with coverage of 68% (#611) · googleapis/python-spanner-django@92ad508 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 92ad508

Browse files
authored
feat: added unit test with coverage of 68% (#611)
Add unit tests for many spanner_django modules that add coverage beyond the built-in django tests.
1 parent 3fa1aeb commit 92ad508

14 files changed

+1057
-103
lines changed

noxfile.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ def lint_setup_py(session):
6666
def default(session):
6767
# Install all test dependencies, then install this package in-place.
6868
session.install(
69-
"django~=2.2", "mock", "mock-import", "pytest", "pytest-cov"
69+
"django~=2.2",
70+
"mock",
71+
"mock-import",
72+
"pytest",
73+
"pytest-cov",
74+
"coverage",
7075
)
7176
session.install("-e", ".")
7277

@@ -79,7 +84,7 @@ def default(session):
7984
"--cov-append",
8085
"--cov-config=.coveragerc",
8186
"--cov-report=",
82-
"--cov-fail-under=20",
87+
"--cov-fail-under=68",
8388
os.path.join("tests", "unit"),
8489
*session.posargs
8590
)

tests/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import os
2+
import django
3+
from django.conf import settings
4+
5+
# We manually designate which settings we will be using in an environment
6+
# variable. This is similar to what occurs in the `manage.py` file.
7+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings")
8+
9+
10+
# `pytest` automatically calls this function once when tests are run.
11+
def pytest_configure():
12+
settings.DEBUG = False
13+
django.setup()

tests/settings.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Use of this source code is governed by a BSD-style
4+
# license that can be found in the LICENSE file or at
5+
# https://developers.google.com/open-source/licenses/bsd
6+
7+
DEBUG = True
8+
USE_TZ = True
9+
10+
INSTALLED_APPS = [
11+
"django_spanner", # Must be the first entry
12+
"django.contrib.contenttypes",
13+
"django.contrib.auth",
14+
"django.contrib.sites",
15+
"django.contrib.sessions",
16+
"django.contrib.messages",
17+
"django.contrib.staticfiles",
18+
"tests",
19+
]
20+
21+
TIME_ZONE = "UTC"
22+
23+
DATABASES = {
24+
"default": {
25+
"ENGINE": "django_spanner",
26+
"PROJECT": "emulator-local",
27+
"INSTANCE": "django-test-instance",
28+
"NAME": "django-test-db",
29+
}
30+
}
31+
SECRET_KEY = "spanner emulator secret key"
32+
33+
PASSWORD_HASHERS = [
34+
"django.contrib.auth.hashers.MD5PasswordHasher",
35+
]
36+
37+
SITE_ID = 1
38+
39+
CONN_MAX_AGE = 60
40+
41+
ENGINE = "django_spanner"
42+
PROJECT = "emulator-local"
43+
INSTANCE = "django-test-instance"
44+
NAME = "django-test-db"
45+
OPTIONS = {}
46+
AUTOCOMMIT = True

tests/unit/django_spanner/__init__.py

Whitespace-only changes.

tests/unit/django_spanner/models.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Use of this source code is governed by a BSD-style
4+
# license that can be found in the LICENSE file or at
5+
# https://developers.google.com/open-source/licenses/bsd
6+
"""
7+
Different models used for testing django-spanner code.
8+
"""
9+
from django.db import models
10+
11+
12+
# Register transformations for model fields.
13+
class UpperCase(models.Transform):
14+
lookup_name = "upper"
15+
function = "UPPER"
16+
bilateral = True
17+
18+
19+
models.CharField.register_lookup(UpperCase)
20+
models.TextField.register_lookup(UpperCase)
21+
22+
23+
# Models
24+
class ModelDecimalField(models.Model):
25+
field = models.DecimalField()
26+
27+
28+
class ModelCharField(models.Model):
29+
field = models.CharField()
30+
31+
32+
class Item(models.Model):
33+
item_id = models.IntegerField()
34+
name = models.CharField(max_length=10)
35+
created = models.DateTimeField()
36+
modified = models.DateTimeField(blank=True, null=True)
37+
38+
class Meta:
39+
ordering = ["name"]
40+
41+
42+
class Number(models.Model):
43+
num = models.IntegerField()
44+
decimal_num = models.DecimalField(max_digits=5, decimal_places=2)
45+
item = models.ForeignKey(Item, models.CASCADE)
46+
47+
48+
class Author(models.Model):
49+
name = models.CharField(max_length=40)
50+
last_name = models.CharField(max_length=40)
51+
num = models.IntegerField(unique=True)
52+
created = models.DateTimeField()
53+
modified = models.DateTimeField(blank=True, null=True)
54+
55+
56+
class Report(models.Model):
57+
name = models.CharField(max_length=10)
58+
creator = models.ForeignKey(Author, models.CASCADE, null=True)
59+
60+
class Meta:
61+
ordering = ["name"]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Use of this source code is governed by a BSD-style
4+
# license that can be found in the LICENSE file or at
5+
# https://developers.google.com/open-source/licenses/bsd
6+
7+
from django_spanner.client import DatabaseClient
8+
from django_spanner.base import DatabaseWrapper
9+
from django_spanner.operations import DatabaseOperations
10+
from unittest import TestCase
11+
import os
12+
13+
14+
class SpannerSimpleTestClass(TestCase):
15+
@classmethod
16+
def setUpClass(cls):
17+
cls.PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"]
18+
19+
cls.INSTANCE_ID = "instance_id"
20+
cls.DATABASE_ID = "database_id"
21+
cls.USER_AGENT = "django_spanner/2.2.0a1"
22+
cls.OPTIONS = {"option": "dummy"}
23+
24+
cls.settings_dict = {
25+
"PROJECT": cls.PROJECT,
26+
"INSTANCE": cls.INSTANCE_ID,
27+
"NAME": cls.DATABASE_ID,
28+
"user_agent": cls.USER_AGENT,
29+
"OPTIONS": cls.OPTIONS,
30+
}
31+
cls.db_client = DatabaseClient(cls.settings_dict)
32+
cls.db_wrapper = cls.connection = DatabaseWrapper(cls.settings_dict)
33+
cls.db_operations = DatabaseOperations(cls.connection)

tests/unit/django_spanner/test_base.py

Lines changed: 29 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,24 @@
44
# license that can be found in the LICENSE file or at
55
# https://developers.google.com/open-source/licenses/bsd
66

7-
import sys
8-
import unittest
9-
import os
10-
11-
from mock_import import mock_import
127
from unittest import mock
8+
from tests.unit.django_spanner.simple_test import SpannerSimpleTestClass
139

1410

15-
@mock_import()
16-
@unittest.skipIf(
17-
sys.version_info < (3, 6), reason="Skipping Python versions <= 3.5"
18-
)
19-
class TestBase(unittest.TestCase):
20-
PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"]
21-
INSTANCE_ID = "instance_id"
22-
DATABASE_ID = "database_id"
23-
USER_AGENT = "django_spanner/2.2.0a1"
24-
OPTIONS = {"option": "dummy"}
25-
26-
settings_dict = {
27-
"PROJECT": PROJECT,
28-
"INSTANCE": INSTANCE_ID,
29-
"NAME": DATABASE_ID,
30-
"user_agent": USER_AGENT,
31-
"OPTIONS": OPTIONS,
32-
}
33-
34-
def _get_target_class(self):
35-
from django_spanner.base import DatabaseWrapper
36-
37-
return DatabaseWrapper
38-
39-
def _make_one(self, *args, **kwargs):
40-
return self._get_target_class()(*args, **kwargs)
41-
11+
class TestBase(SpannerSimpleTestClass):
4212
def test_property_instance(self):
43-
settings_dict = {"INSTANCE": "instance"}
44-
db_wrapper = self._make_one(settings_dict=settings_dict)
45-
4613
with mock.patch("django_spanner.base.spanner") as mock_spanner:
4714
mock_spanner.Client = mock_client = mock.MagicMock()
4815
mock_client().instance = mock_instance = mock.MagicMock()
49-
_ = db_wrapper.instance
50-
mock_instance.assert_called_once_with(settings_dict["INSTANCE"])
16+
_ = self.db_wrapper.instance
17+
mock_instance.assert_called_once_with(self.INSTANCE_ID)
5118

52-
def test_property__nodb_connection(self):
53-
db_wrapper = self._make_one(None)
19+
def test_property_nodb_connection(self):
5420
with self.assertRaises(NotImplementedError):
55-
db_wrapper._nodb_connection()
21+
self.db_wrapper._nodb_connection()
5622

5723
def test_get_connection_params(self):
58-
db_wrapper = self._make_one(self.settings_dict)
59-
params = db_wrapper.get_connection_params()
24+
params = self.db_wrapper.get_connection_params()
6025

6126
self.assertEqual(params["project"], self.PROJECT)
6227
self.assertEqual(params["instance_id"], self.INSTANCE_ID)
@@ -65,54 +30,50 @@ def test_get_connection_params(self):
6530
self.assertEqual(params["option"], self.OPTIONS["option"])
6631

6732
def test_get_new_connection(self):
68-
db_wrapper = self._make_one(self.settings_dict)
69-
db_wrapper.Database = mock_database = mock.MagicMock()
33+
self.db_wrapper.Database = mock_database = mock.MagicMock()
7034
mock_database.connect = mock_connection = mock.MagicMock()
7135
conn_params = {"test_param": "dummy"}
72-
db_wrapper. 10000 get_new_connection(conn_params)
36+
self.db_wrapper.get_new_connection(conn_params)
7337
mock_connection.assert_called_once_with(**conn_params)
7438

7539
def test_init_connection_state(self):
76-
db_wrapper = self._make_one(self.settings_dict)
77-
db_wrapper.connection = mock_connection = mock.MagicMock()
40+
self.db_wrapper.connection = mock_connection = mock.MagicMock()
7841
mock_connection.close = mock_close = mock.MagicMock()
79-
db_wrapper.init_connection_state()
42+
self.db_wrapper.init_connection_state()
8043
mock_close.assert_called_once_with()
8144

8245
def test_create_cursor(self):
83-
db_wrapper = self._make_one(self.settings_dict)
84-
db_wrapper.connection = mock_connection = mock.MagicMock()
46+
self.db_wrapper.connection = mock_connection = mock.MagicMock()
8547
mock_connection.cursor = mock_cursor = mock.MagicMock()
86-
db_wrapper.create_cursor()
48+
self.db_wrapper.create_cursor()
8749
mock_cursor.assert_called_once_with()
8850

89-
def test__set_autocommit(self):
90-
db_wrapper = self._make_one(self.settings_dict)
91-
db_wrapper.connection = mock_connection = mock.MagicMock()
51+
def test_set_autocommit(self):
52+
self.db_wrapper.connection = mock_connection = mock.MagicMock()
9253
mock_connection.autocommit = False
93-
db_wrapper._set_autocommit(True)
54+
self.db_wrapper._set_autocommit(True)
9455
self.assertEqual(mock_connection.autocommit, True)
9556

9657
def test_is_usable(self):
97-
from google.cloud.spanner_dbapi.exceptions import Error
98-
99-
db_wrapper = self._make_one(self.settings_dict)
100-
db_wrapper.connection = None
101-
self.assertFalse(db_wrapper.is_usable())
58+
self.db_wrapper.connection = None
59+
self.assertFalse(self.db_wrapper.is_usable())
10260

103-
db_wrapper.connection = mock_connection = mock.MagicMock()
61+
self.db_wrapper.connection = mock_connection = mock.MagicMock()
10462
mock_connection.is_closed = True
105-
self.assertFalse(db_wrapper.is_usable())
63+
self.assertFalse(self.db_wrapper.is_usable())
10664

10765
mock_connection.is_closed = False
108-
self.assertTrue(db_wrapper.is_usable())
66+
self.assertTrue(self.db_wrapper.is_usable())
67+
68+
def test_is_usable_with_error(self):
69+
from google.cloud.spanner_dbapi.exceptions import Error
10970

71+
self.db_wrapper.connection = mock_connection = mock.MagicMock()
11072
mock_connection.cursor = mock.MagicMock(side_effect=Error)
111-
self.assertFalse(db_wrapper.is_usable())
73+
self.assertFalse(self.db_wrapper.is_usable())
11274

113-
def test__start_transaction_under_autocommit(self):
114-
db_wrapper = self._make_one(self.settings_dict)
115-
db_wrapper.connection = mock_connection = mock.MagicMock()
75+
def test_start_transaction_under_autocommit(self):
76+
self.db_wrapper.connection = mock_connection = mock.MagicMock()
11677
mock_connection.cursor = mock_cursor = mock.MagicMock()
117-
db_wrapper._start_transaction_under_autocommit()
78+
self.db_wrapper._start_transaction_under_autocommit()
11879
mock_cursor.assert_called_once_with()

tests/unit/django_spanner/test_client.py

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,12 @@
44
# license that can be found in the LICENSE file or at
55
# https://developers.google.com/open-source/licenses/bsd
66

7-
import sys
8-
import unittest
9-
import os
107

8+
from google.cloud.spanner_dbapi.exceptions import NotSupportedError
9+
from tests.unit.django_spanner.simple_test import SpannerSimpleTestClass
1110

12-
@unittest.skipIf(
13-
sys.version_info < (3, 6), reason="Skipping Python versions <= 3.5"
14-
)
15-
class TestClient(unittest.TestCase):
16-
PROJECT = os.environ["GOOGLE_CLOUD_PROJECT"]
17-
INSTANCE_ID = "instance_id"
18-
DATABASE_ID = "database_id"
19-
USER_AGENT = "django_spanner/2.2.0a1"
20-
OPTIONS = {"option": "dummy"}
21-
22-
settings_dict = {
23-
"PROJECT": PROJECT,
24-
"INSTANCE": INSTANCE_ID,
25-
"NAME": DATABASE_ID,
26-
"user_agent": USER_AGENT,
27-
"OPTIONS": OPTIONS,
28-
}
29-
30-
def _get_target_class(self):
31-
from django_spanner.client import DatabaseClient
32-
33-
return DatabaseClient
34-
35-
def _make_one(self, *args, **kwargs):
36-
return self._get_target_class()(*args, **kwargs)
3711

12+
class TestClient(SpannerSimpleTestClass):
3813
def test_runshell(self):
39-
from google.cloud.spanner_dbapi.exceptions import NotSupportedError
40-
41-
db_wrapper = self._make_one(self.settings_dict)
42-
4314
with self.assertRaises(NotSupportedError):
44-
db_wrapper.runshell(parameters=self.settings_dict)
15+
self.db_client.runshell(parameters=self.settings_dict)

0 commit comments

Comments
 (0)
0