哈啰,我们又见面了,今天我们来看看怎么测试一个网站,在开始实作之前,我们必须先釐清一些观念,包含 架站的过程中会遇到什么事情、为什么需要 测试。
架站流程
我简单介绍一下要架站,需要什么流程,其中 需求与设计、开发、测试 与 布署 这四个步骤的流程,会依照你们团队所採用的 开发方法 而有所不同,这是软体工程中的一部分,典型旧型的开发方法是 瀑布模型(Waterfall
),以及新型热门的敏捷开发方法有 Scrum、XP 等等,至于怎么挑选适合团队的开发方法,会有团队成员的性格、专案性质等等的变因,再讲下去就扯远了,我们回到流程。
python
与 django
的理解而跟着变动开发就是实际产生网站功能的地方测试测试 功能 是否符合 需求有些公司可能将测试独立出一个 QA 部门 或 测试人员,来负责这部分布署将网站放到一个地方给人使用,而这个地方可能是 公司内部的伺服器,可能是 云端伺服器,也可能是我自己的电脑 等等前面 Day1~Day10 已经走到开发的阶段,接下来我们来看看测试。
测试,对于开发的重要性 (痛点在哪里?)
在现在的软体产业中,需求变化非常快,有可能今天跟你说我需要部落格,明天就跟你说部落格先不要做了,我现在需要购物车,那么这时候,身为开发人员的你,该怎么维持你的程式码品质 ?
你可能会说「我光是要开发就已经来不及了,我怎么可能还要顾我的品质 ?,都是客户或老闆的错啊,他们不应该这样变来变去,这样怎么可能把事情做好呢 !」,但事实就是这样,山不转路转,你势必需要去习惯这样的文化,所以再回到问题,如何兼顾 开发速度、开发弹性 与 程式码品质 ?
在 TDD 的观念中,品质就是从 测试 来的,当你的测试够明确,你很知道你要达到什么样的效果,就是一个好程式码、一个好功能,但你又会说「我开发就已经不够时间了,怎么还有时间写测试 !」,但问题是,你先写好测试,才有机会省下开发的时间,才能继续写测试,可是没有时间怎么写测试,那你没有写测试怎么有时间,所以才要先写测试才有时间阿 ...
恩 ... 相信你感受到这个矛盾的现象了。总之,我们就是想办法写好测试,开发 就尽量去符合 测试 的目标。
单元测试 (Unit Testing)
单元测试就是「最小单位的测试」,可以小到某个 class 底下的变数型别,也可以到 class 底下的 method 行为,是否符合预期。
shop/models.py
这是我们在 Day9 所完成的 model。
from django.db import modelsfrom django.utils import timezoneimport osdef get_image_path(instance, filename): return os.path.join('uploads', filename)class Product(models.Model): # basic info name = models.CharField(max_length=100, blank=False) price = models.DecimalField(blank=False, max_digits=100, decimal_places=0) img = models.ImageField(upload_to=get_image_path, default=get_image_path(instance=0, filename='product-1.jpg')) # discount on_sale = models.BooleanField(blank=True, null=True) tag = models.CharField(max_length=20, blank=True, null=True) percent_off = models.DecimalField(blank=True, null=True, max_digits=30, decimal_places=1) sale_price = models.DecimalField(blank=True, null=True, max_digits=30, decimal_places=0) # for analysis bought_counter = models.DecimalField(default=0, max_digits=30, decimal_places=0) created_date = models.DateTimeField(default=timezone.now) published_date = models.DateTimeField(blank=True, null=True) def publish(self): self.published_date = timezone.now self.save() def __str__(self): return self.name
shop/tests/test_models.py
我们先测试一下 field type 是不是符合预期,这边只举出四个栏位,省略其他栏位。
from django.test import TestCasefrom django.db.models import DecimalField, CharField, \ ImageField, BooleanField, DateTimeFieldfrom shop.models import Productclass TestProductFieldType(TestCase): def test_name_field_type(self): assert_same_type(self, "name", CharField) def test_price_field_type(self): assert_same_type(self, "price", DecimalField) def test_img_field_type(self): assert_same_type(self, "img", ImageField) def test_onsale_field_type(self): assert_same_type(self, "on_sale", BooleanField)...def assert_same_type(self, field_name, field_type): self.assertTrue( isinstance( get_product_field(field_name), field_type ) )def get_product_field(field_name): return Product._meta.get_field(field_name)
现在的专案架构
别忘了先把 django 跑起来
$ python manage.py runserver
单元测试跑起来
$ python manage.py test shop.tests.test_models
这边指定只要跑 test_models.py
这个档案里面的测试项目。
单元测试结果
因为实际上我把十个栏位都写进测试了,所以出来会有 10 tests。(先忽略我的 branch 名称,因为我是做完整个 CI 流程才回来写文章的)
单元测试结束,接着来到 整合测试 (Integration Testing)
我们在这边先写个简单的整合测试,来测试网站的其中一个页面是否正常。
shop/tests/test_views.py
我们针对 views.py
里面的 shop_view
,来测试 shop_view
这个页面的 function name
、reachable
、template file name
、page title
等等属性。
from django.test import TestCasefrom django.urls import resolvefrom shop.views import shop_viewclass TestShopPageView(TestCase): def test_resolve_shop(self): found = resolve('/') self.assertEqual(found.func.__name__, shop_view.__name__) # 期望 found.func.__name__ 会等于 shop_view def test_reachable_shop(self): response = self.client.get('/') self.assertEqual(response.status_code, 200)# 期望 status_code 会等于 200 (也就是正常) def test_template_shop(self): response = self.client.get('/') self.assertTemplateUsed(response, 'shop/shop.html')# 期望这页面的 template 是 shop/shop.html 这个档案 def test_title_shop(self): response = self.client.get('/') self.assertContains(response, 'RS Django Shop')# 期望这页面的标题是 RS Django Shop
整合测试结果
(先忽略我的 branch 名称,因为我是做完整个 CI 流程才回来写文章的)
如此一来,将来我在开发的时候,可能不小心按到键盘,更改到了页面标题名称变成 RS Django Sho
,我在跑测试的时候就会抓到,不会等到别人来告诉我。也有一种情况是,你去上厕所忘记锁定,然后你家的猫跳上桌键盘踩踩踩 XD。
测试所带来的效益
当你写了 单元测试
与 整合测试
后,加上有 版本控制
的辅助,你可以 更加大胆地开发新功能、更加大胆地重构,因为有这些工具存在,开发者可以 不用那么怕 牵一髮动全身的窘境。
单日心得总结
我必须先澄清,我假日并没有偷懒 QQ,因为我又犯上次犯过的错误,就是把单篇範围又订得太大了,导致一直不知道怎么收尾。
这篇文章我改了超多次,原定的主题是「试玩 CircleCI
,体验持续整合」,等我注册完 CircleCI
、绑定 Github
专案后,我发现我根本还没写测试,怎么持续整合 QQ,再来把测试写完,在本地端测试成功了,我就在思考一个问题,那... 我该怎么在 CI
上面执行我的网站 !?,执行了网站才能跑测试阿,心想「该死,我把顺序搞反了...」,可是这时候已经过完一整天了,也无法有系统的写成文章。
隔天我花了一整天研究 Docker,试图想要把我的执行环境包在 Docker
里面,然后再放到 CircleCI
里面去执行我的网站,最后才能跑测试,然而 Docker
这东西真的不是我想像的那么简单,就这样,一整天又过了,我还是无法写成文章,所以就到今天了。
一直违反自己的承诺的感觉是真的很差,尤其又是公开宣布的承诺,不知道你们有没有这种感觉,明明想要遵守承诺,可是又不想让文章沦为散落的杂项日记,自己心理也很清楚,不是因为偷懒造成的,也不是刻意要违背承诺,总之,这种感觉不太妙。
所以呢,今天我们体验了 测试 带来的效益,接下来我们试着了解 Docker
能带给我们什么感受吧 !
我是 RS,这是我的 不做怎么知道系列 文章,我们 明天见。
喜欢我的文章吗? 赶快来看看我都发了什么文章吧:我的文章目录欢迎阅读我的上一篇: [不做怎么知道系列之Android开发者的30天后端养成故事 Day10] - 你/妳SOLID了吗? #什么是SOLID Principles #减少debug时间 #高品质程式欢迎阅读我的下一篇: [不做怎么知道系列之Android开发者的30天后端养成故事 Day12] - 容器vs.鸭子vs.搬运工 #Docker #什么是容器? #可转移的执行环境