Compare commits

..

3 Commits

Author SHA1 Message Date
Jason Zhu 22a38297a9 Added conclusion of covering in chap3.md 2020-11-07 15:32:20 +11:00
Jason Zhu 9fa1028ce1 Finished chap3 2020-11-07 15:27:12 +11:00
Jason Zhu 5e2f64346d Finished up to 3.5 2020-11-06 12:34:46 +11:00
4 changed files with 151 additions and 5 deletions

3
.gitmodules vendored 100644
View File

@ -0,0 +1,3 @@
[submodule "src"]
path = python-tdd-book-src
url = ssh://git@lj918plus.dsmynas.com:2223/Jason/python-tdd-book-src.git

@ -0,0 +1 @@
Subproject commit 76a8bf8aed1c901a3ff6a1f04c743b8230b79f50

View File

@ -6,7 +6,7 @@ Chap2 introduced writing a functional test using unittest module (expected failu
* Django encourage structure the code into apps. We can reuse app developed by us or others
Create an app `lists` (sub-directory) via `python manage.py startapp lists` along other `functional_tests.py` & `manage.py`, this subdir should include `tests.py` be default
Create an app `lists` (sub-directory) via `python manage.py startapp lists` in the directory which contains `functional_tests.py` & `manage.py`, this subdir should include `tests.py` be default
## 3.2 Unit Tests, and How They Differ from Functional Tests
@ -47,11 +47,11 @@ python manage.py test
## 3.4 Django's MVC, URLs, and View Functions
Django is structured along a classic [**Model-View-Controller (MVC)** pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
Django is structured along classic [**Model-View-Controller (MVC)** pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
Django's main job is to decide what to do when a user asks for a particular URL on our site.
Django's workflow is like:
Django's workflow:
1. An HTTP `request` comes in for a particular URL
2. *Resolving the URL*: Django uses some rules to decide which `view` function should deal with the request.
@ -62,9 +62,33 @@ Let's design a tests:
1. Verify we can resolve the URL for the root of site ("/") to a particular view function we've made?
2. Verify view function return some HTML which will get the functional test to pass?
```python
from django.test import TestCase
from django.urls import resolve
from lists.views import home_page
class HomePageTest(TestCase):
def test_root_url_resolves_to_home_page_view(self):
found = resolve('/')
self.assertEqual(found.func, home_page)
```
Where:
* The resolve() function if `django.url` package can be used for resolving URL paths to the corresponding view functions. The function returns a [`ResolverMatch` object](https://docs.djangoproject.com/en/3.1/ref/urlresolvers/#django.urls.ResolverMatch) that allows you to access various metadata about the resolved URL.
* In `assertEqual(...)`, we try to test that `found.func` is `home_page` (a view function we haven't defined yet)
Result of running test:
```
ImportError: cannot import name 'home_page'
```
## 3.5 At Last! We Actually Write Some Application Code!
Add content in `lists/views.py`, to pass the test
In 3.4, we created a `test_root_url_resolves_to_home_page_view()` test case that check "django can resolve request to root and return a view function"
Hence, add content in `lists/views.py`, we can pass the test
```python
from django.shortcuts import render
@ -72,10 +96,122 @@ from django.shortcuts import render
home_page = None
```
Running the test generate following info
```
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_root_url_resolves_to_home_page_view (lists.tests.HomePageTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/jason/HomeWorkstation/SynologyGiteaSpace/python-tdd-book-src/src/lists/tests.py", line 8, in test_root_url_resolves_to_home_page_view
found = resolve('/')
File "/home/jason/miniconda3/envs/python-tdd-book/lib/python3.6/site-packages/django/urls/base.py", line 27, in resolve
return get_resolver(urlconf).resolve(path)
File "/home/jason/miniconda3/envs/python-tdd-book/lib/python3.6/site-packages/django/urls/resolvers.py", line 394, in resolve
raise Resolver404({'tried': tried, 'path': new_path})
django.urls.exceptions.Resolver404: {'tried': [[<RegexURLResolver <RegexURLPattern list> (admin:admin) ^admin/>]], 'path': ''}
----------------------------------------------------------------------
Ran 1 test in 0.001s
```
* error due to `django.urls.exceptions.Resolver404: {'tried': [[<RegexURLResolver <RegexURLPattern list> (admin:admin) ^admin/>]], 'path': ''}`
* Happen in `python-tdd-book-src/src/lists/tests.py", line 8, in test_root_url_resolves_to_home_page_view`
Overall, the traceback can be interpreted as: "when trying to resolve `/`, Django raised a 404 error". i.e. Django can't find a URL mapping for "/"
## 3.6 urls.py
Django use `urls.py` in each app to map URLs to view functions.
```python
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
```
Note:
* This book use Django v1.11. So `path` function is not utilized, it's used in v3.x
Explain:
* `url` start with a regex, which defines which URLs it looks for, and where (function) should these request to send
* `^$` means empty string
Modifying `superlists.urls` to
```python
from django.conf.urls import url
from lists import views
urlpatterns = [
url(r'^$', views.home_page, name='home'),
]
```
Generate error as shown
```
...
File "/home/jason/HomeWorkstation/SynologyGiteaSpace/python-tdd-book-src/src/superlists/urls.py", line 20, in <module>
url(r'^$', views.home_page, name='home'),
File "/home/jason/miniconda3/envs/python-tdd-book/lib/python3.6/site-packages/django/conf/urls/__init__.py", line 85, in url
raise TypeError('view must be a callable or a list/tuple in the case of include().')
TypeError: view must be a callable or a list/tuple in the case of include().
```
Analyzing error message "TypeError: view must be a callable or a list/tuple in the case of include().": unit tests have actually made the link btw the URL "/" and `home_page = None` in `lists/views.py`, and are now complaining that `home_page` view is not callable.
Fix this problem via modifying `lists/views.py`
```python
def home_page():
pass
```
## 3.7 Unit Testing a View
After creating simple `home_page` empty function. We can create test function in `lists/tests.py`
```python
def test_home_page_returns_correct_html(self):
request = HttpRequest()
response = home_page(request)
html = response.content.decode('utf8')
self.assertTrue(html.startswith('<html>'))
self.assertIn('<title>To-Do list</title>', html)
self.assertTrue(html.endswith('</html>'))
```
Result of `python manager.py test` is:
```
line 15, in test_home_page_returns_correct_html
response = home_page(request)
TypeError: home_page() takes 0 positional arguments but 1 was given
```
### 3.7.1 The Unit-Test/Code Cycle
TDD *unit-test/code cycle*:
1. In the terminal, run unit tests and see how they fail
2. In the editor, create minimal code change to fix test failure.
3. Repeat
Fix the `views.py` to
```python
def home_page(request):
return HttpResponse('<html><title>To-Do list</title></html>')
```
Conclusion of covering:
* Starting a Django app
* The Django unit test runner
* The difference btw FTs and unit tests
* Django URL resolving and `urls.py`
* Django view functions, request and response objects
* And returning basic HTML

View File

@ -0,0 +1,6 @@
# Summary
Summarize learning, for quick reference
* Run Django server: `python manage.py runserver`
* If test cases are created by framework, run it via: `python manage.py test`