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 * 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 ## 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 ## 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 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 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. 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? 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? 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! ## 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 ```python
from django.shortcuts import render from django.shortcuts import render
@ -72,10 +96,122 @@ from django.shortcuts import render
home_page = None 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 ## 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 ## 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 ### 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`