《构建之法》 & Git+ & CI/CD

个人阅读作业#2

项目 内容
本作业所属课程 2020春季软件工程(罗杰 任健)
本作业要求 个人阅读作业#2
我的课程目标 具备一个软件工程师所需要的素质
本作业帮助 整体上软件工程,熟悉版本控制以及CI/CD工具

一、阅读提问

单元测试

(P26)单元测试的运行/通过/失败/不依赖于别的测试,可以人为构造数据,以保持单元测试的独立性。

面对需要处理大量数据的模块,人为构造数据就最造成很大的重复性工作。比如上学期写编译器的时候,为了测试我的词法分析模块的正确性,对一个十分简单的源程序,我就需要写上百行的期望输出,而且单元测试运行完之后,对那些错误部分进行分析,发现大部分并不是我要测试的语法分析模块的错误,反而而是我手动构造的期望输出的错误。在对语法分析部分进行测试的时候,这种情况尤甚。有的时候为了构造样例去写一些测试样例的生成器,但是生成器本身也会发生错误。

针对处理数据量较大的模块,我们该怎样手动构造测试样例呢?还是说,我们应该换一种单元测试的粒度?

断言

(P70)当你觉得某事肯定如何时,就可以用断言。

虽然书中上下文比较的是断言和异常处理的问题,但是断言(assert)很长时间来是我用着很纠结的地方。就我以往用断言的经验来看,主要在以下一些地方:

  1. 自己不打算写单元测试,用断言来确定程序运行到某个部分确实是按照我所期望的;
  2. 为了获得IDE的辅助,比如当我确定某个向下转型是安全的时候,写了assert obj instanceof MyClass;,后面的语句需要将obj强制转换为MyCalss时,IDE就会帮我添上强转;
  3. 相当于一句注释;

但是有单元测试的情况下,第一类的情况还需要写断言吗?

结对编程

(P80)总之,如果运用得当,结对编程可以取得更高的投入产出比。

这句话给我的第一感觉,就像这句话一样:

如果方法得当,高考前一个月也可以提高300分。

虽然我的类比有些夸张,但意思是一样的:后半句话看起来那样美好,但是它的前提条件就有些说不太清楚了。

书中随后介绍了结对编程的流程与分工,也列举了一些注意事项,甚至还提到了一些结对编程中的交流技巧。但是却忽略了一个问题:什么样的人/团队适合结对编程呢?

后文提到了两人合作的阶段:萌芽\(\rightarrow\)磨合\(\rightarrow\)规范\(\rightarrow\)创造。但同时也指出了,并不是所有的合作都能走到最后一步,可能磨合太多后进入“解体”阶段。最终走向失败的合作有可能是是双方的方式不对,但也可能是双方口味不合,甚至有一方深深排斥自己工作的时候有另一个在边上。

国内为何很少有人做结对编程呢?是确实不好还是属于中国特色? - 小白的回答 - 知乎

这个回答中就提到,并不是所有人都能在交流中保持专注,也并不是所有人都喜欢在一个充满交流的环境中工作,甚至说,很多人走上了软件开发的道路就是因为自己需要安静的环境来保持专注。

选择结对编程,就意味着要让两个人去完成一个人的工作,这本身就是不利于投入产出比的,如果结对编程不能带来额外的好处,就是亏的。也就是说如果一组合作伙伴快速走向解体,那么就很可能是一次亏本的尝试。那么相较于关注怎样进行结对编程,我们是不是更应该关注哪些人适合结对、怎样挑选结对的伙伴呢?

典型用户

(P206)怎样才能定义典型用户呢?我们首先要定义用户的角色。正如戏剧中有正面和反面角色,软件系统中也有受欢迎的和不受欢迎的典型用户。

受欢迎的用户是我们软件的目标,自然需要作为典型用户来分析。

但是为什么也要分析不受欢迎的用户?一方面,针对不受欢迎的用户的一些策略,如保密、网络安全等,本身就是软件质量需要考量的一部分,甚至是受欢迎用户的需求,单独把这部分用户拎出来分析也不会起到很大的补充效果。另一方面,确定会有哪些不受欢迎的用户并不容易,比如微信起初就没有把淘宝链接、抖音连接、有偿朋友圈转发等视为不欢迎的,这些问题是随着运营而产生/发现的。

对不受欢迎的用户的分析投入产出比并不高,可以将其去掉吗?

功能说明书

(P215)

第一,定义好相关的概念。第二,规范好一些假设。第三,避免一些误解,界定一些边界条件。第四,描述主流的用户/软件交互步骤。第五,一些好的功能还会有副作用。第六,服务质量的说明。

功能说明书的意义就在于严格地、无歧义的描述软件的外部功能,讲述交互的方式。但是这样的功能说明书必定是很长的,相信大部分用户也不会有闲心来详细阅读功能说明书。那么怎样能快速、有效地引导用户来按照我们设计的方式来与软件进行交互呢?这一部分应当是谁的工作呢?


二、调研源代码版本管理软件

上网调研并了解目前被广泛使用的基于源代码版本管理软件Git的项目管理工具,如GitHub、Gitlab、Bitbucket 等,比较它们之间的异同(包括但不限于团队协作流程,项目管理等)。

我个人用giteegithub比较多,之前OO课使用过gitlab但是并没有充分体验其中的功能。

总的来说二者差异都不大,都支持sshkey、私有/开源、fork、star、pull-request、issues、wiki、在线编辑、gitignore/LICENSE模板、统计等。

GitHub Gitee
服务器 国外 国内
权限管理 需要付费或者开源 免费即可进行权限管理
私有仓库人数 单个仓库不超过5人 个人的全部私有仓库的成员总数不超过5人
CI/CD Github Action 支持百度、腾讯云的服务,官方服务还在测试中

github由于受众更广,有更多知名项目,gitee也提供了直接从github导入的功能。

gitlab相比二者,最大的特点在于可以免费部署私服,私服可以获得管理员权限,自由度更高。


三、调研持续集成/部署工具

调研一下持续集成(Continuous Integration, CI)、持续部署(Continuous Delivery, CD)工具(例如:Gitlab CI、Github Action、Travis等),请你选择至少2个持续集成解决方案来进行动手实践,对每个解决方案的要求如下:

.NET 5 + Github Action

仓库链接 : https://github.com/SnowPhoenix0105/SimpleCalculator

实验结果:

yml配置文件:

name: .NET

on:
push:
branches: [ master ]
pull_request:
branches: [ master ] jobs:
build: runs-on: ubuntu-latest steps:
- uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build -v n

由于使用的是Microsoft当前正在推进的.NET 5,所以Github Action对此支持是比较好的,大致直接使用模板即可。下面简单介绍一下各个step的作用

step1 环境配置

- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.x

这一步是向环境中加载.NET 5环境。

step2 还原依赖

- name: Restore dependencies
run: dotnet restore

对于外部依赖项,我们一般不作为仓库的一部分,而是将其信息保存到相关文件中(.NET 就是 sln/csproj 文件),在构建时通过包管理器重新加载。

step3 构建

- name: Build
run: dotnet build --no-restore

由于dotnet build命令默认会执行一遍restore,但是我们前面已经手动完成了,所以这里要指定不再进行restore操作。

step4 测试

- name: Test
run: dotnet test --no-build -v n

同理step3,dotnet test命令默认会执行一遍build,这里通过选项指定不再重新构建。

-v n全称为--verbosity normal,用于指示输出的信息等级,normal等级下将输出所有进行的测试,并输出测试结果以及测试用时。默认选项为m[inial],对于通过的测试,仅做最终统计,对于未通过的测试才有详细信息。

值得注意的是,由于我在顶层目录创建了解决方案SimpleCalculator.sln并将SimpleCalculatorSimpleCalculator.Test两个工程添加到其中,故而所有dotnet命令都没有指定工程,而是缺省使用了顶层的sln作为目标。

此外step2~4中,显然后一项都会缺省执行前一项,这里我们却故意将其拆开,手动一步一步执行,其目的一方面可以更快定位哪一步出错,另一方面也方便调节输出信息的等级,使得不同步骤的结果信息能够相互隔离。

Maven + JUnit + Gitlab CI

由于学校的gitlab我没有权限新建仓库,我选择了OO课程的pre作业,创建新的cicd分支。并且由于我也没有权限将其设置为public,所以代码部分我在gitee上放了一份作备用,但是CI/CD功能使用的是gitlab的。

仓库链接:https://gitlab.buaaoo.top/oo_2019_homeworks/oo_2020_preview2_18231045_pre2_task6/tree/cicd

备用仓库链接:https://gitee.com/snowphoenix/primary-geometry

实验结果:

我这里选用了一张包括单元测试不通过的情况的截图。

yml配置文件:

image: local-registry.inner.buaaoo.top/image-dev/java:8u201

stages:
- build
- test before_script:
- java -version
- javac -version
- mvn -v mvn_build:
stage: build
script:
- echo "Build Project"
- mvn compile -q mvn_test:
stage: test
dependencies:
- mvn_build
script:
- echo "Run JUnit"
- mvn test -q
- echo "Code coverage 99%"
coverage: '/Code coverage \d+/'

大致直接使用了课程组提供的模板,稍加改造。下面简单分析一下各个步骤的作用:

step1 显示版本

before_script:
- java -version
- javac -version
- mvn -v

step2 构建

mvn_build:
stage: build
script:
- echo "Build Project"
- mvn compile -q

我这里为maven的编译选项加了-q,主要是mvn下载外部依赖的包时,会产生大量信息,掩盖掉有用信息。

step3 单元测试

mvn_test:
stage: test
dependencies:
- mvn_build
script:
- echo "Run JUnit"
- mvn test -q
- echo "Code coverage 99%"
coverage: '/Code coverage \d+/'

dependencies选项制定了依赖的动作,表示测试需要在构建完成之后进行。

和构建相同,maven下载外部依赖的包的时候会产生大量信息,掩盖有用信息,尤其是单元测试的结果信息相较于构建信息可能更加重要,所以也加了-q选项,帮助我快速找到哪些单元测试没有通过。

echo "Code coverage 99%"只是为了测试coverage: '/Code coverage \d+/',后者的作用是通过正则匹配,从运行的信息中提取出覆盖率信息,显示在gitlab的UI上,真实情况下应该不会通过echo命令来输出覆盖率。

CI/CD小结

Github ActionGitlab CI相比,直观上感觉前者的视觉设计更加合理,各个步骤的信息都是分开的,可以方便找到需要的信息,而后者的信息就很庞杂,一个外部依赖包的下载就可能导致整整一页的信息,不得不打开-q选项,但这样又可能丢失有用信息。这一点也可能是因为我对maven或者CI/CD工具不熟悉导致的吧。

就目前的简单实用体验来看,利用CI/CD工具构建相比于本地进行构建的区别大致有如下几点:

  1. 需要代码具有跨平台能力、跨设备能力。我最开始本来想用我之前写的编译器来试一试的,但是我当初写编译器时,单元测试中有大量不可移植代码,比如硬编码的绝对路径,所以无法使用。
  2. CI/CD依赖命令行接口,而本地测试我们大多可以直接通过IDE的GUI界面来完成。
  3. 运行较慢,可能是因为我这几次使用的都是托管的Runner来运行,所以每次push后都需要等待很久才能查看结果,但是本地进行相同的步骤往往就很快;当然,还有一个因素就是本地的环境是固定的,而利用CI/CD功能需要先拉取/配置环境,构建与测试完成后还要进行还原,而本地就不需要。
  4. 共享性,每一个访问仓库的成员都可以看到这次push后的构建的结果,而本地则不然。

【软件工程】《构建之法》 & Git+ & CI/CD的更多相关文章

  1. 构建之法与CI/CD

    项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 个人阅读作业2 我在这个课程的目标是 认识软工,拥抱软工,提升相关能力以便日后与其朝夕相伴 这个作业在哪个具 ...

  2. 软工个人阅读作业2 —— 构建之法与CI/CD

    项目 内容 这个作业属于哪个课程 2021春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人阅读作业#2 我在这个课程的目标是 阅读思考教材,调研软工工具 这个作业在哪个具体方面帮助我实 ...

  3. 《构建之法》& CI/CD调研

    项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 2021年软工-个人阅读作业2 我在这个课程的目标是 提升软件开发能力与团队意识 这个作业在哪个具体方面帮助 ...

  4. 201771030117-祁甜 实验一 软件工程准备—<阅读《现代软件工程——构建之法》提出的三个问题>

    项目 内容 课程班级博客链接 https://edu.cnblogs.com/campus/xbsf/nwnu2020SE 这个作业要求链接 https://www.cnblogs.com/nwnu- ...

  5. 201771010117—马兴德—实验一 软件工程准备—掌握博客中MarkDown的使用以及通读《现代软件工程—构建之法》的总结

    实验一 软件工程的前期准备工作 在前期的准备工作以及老师上课的讲解中,我懂得了"软件=程序+软件工程"这句话的基本含义,以前只是对软件工程有一个很浅显的概念,现在在读了<现代 ...

  6. 201771010131-王之泰 实验一 软件工程准备—<通读《现代软件工程—构建之法》后所思所想>周学习总结

    项目 内容 作业所属课程 https://www.cnblogs.com/nwnu-daizh/ 作业要求 https://www.cnblogs.com/nwnu-daizh/p/12369881. ...

  7. 201771010105—达拉草 实验一 软件工程准备—<软件工程构建之法—初步了解和认识>

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  8. 201871010109-胡欢欢-实验一-软件工程的准备(初识github及《现代软件工程-构建之法》)

    项目 内容 课程班级博客链接 2021年春软件工程课程班(2018级计算机科学与技术) 这个作业要求链接链接 实验一软件工程准备 我的课程学习目标 了解github的基本使用,学习markdown编辑 ...

  9. 软件工程-构建之法 Visual Studio开发平台的安装与单元测试

    一.前言 自从开始了大三下的生活,学校开设一门课程“软件工程”,刚好我们是第一届进行课程改革,不在像以前那样背背概念,考前进行好好突击,然后考试就能过,最后毕业了发现软件工程课程到底我们在其中学习了什 ...

随机推荐

  1. 别再恐惧 IP 协议(万字长文 | 多图预警)

    尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 「CS-Wiki」Gitee ...

  2. CentOS6.4 Install oh-my-zsh

    先安装zsh yum -y install zsh # 查看是否安装完成 cat /etc/shells /bin/sh /bin/bash /sbin/nologin /bin/dash /bin/ ...

  3. DDD实战课--学习笔记

    目录 学好了DDD,你能做什么? 领域驱动设计:微服务设计为什么要选择DDD? 领域.子域.核心域.通用域和支撑域:傻傻分不清? 限界上下文:定义领域边界的利器 实体和值对象:从领域模型的基础单元看系 ...

  4. golang 实现求两向量夹角

    type Vector3 struct { X float64 `json:"x"` Y float64 `json:"y"` Z float64 `json: ...

  5. 翻译:《实用的Python编程》06_01_Iteration_protocol

    目录 | 上一节 (5.2 封装) | 下一节 (6.2 自定义迭代) 6.1 迭代协议 本节将探究迭代的底层过程. 迭代无处不在 许多对象都支持迭代: a = 'hello' for c in a: ...

  6. canvas-修改图片亮度

    canvas操作-修改图片亮度 目录 canvas操作-修改图片亮度 图片亮度的概念 下面用ps截图举一个例子: 调整图片亮度的方案 实现方案一 从RGB到HSV的转换 转换的公式 javascrip ...

  7. linux安装nginx 并配置文件服务器和代理服务器

    linux安装nginx搭建服务并实现文件服务器和代理服务器配置 1.课题的背景和意义 由于编码过程中需要进行文件上传服务,文件上传后 需要有http资源的路径需要访问.原则上可以通过Apache . ...

  8. 一文教你搞懂 Go 中栈操作

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/513 本文使用的go的源码15.7 知识点 LInux 进程在内存布 ...

  9. C++并发与多线程学习笔记--互斥量、用法、死锁概念

    互斥量(mutex)的基本概念 互斥量的用法 lock(), unlock() std::lock_guard类模板 死锁 死锁演示 死锁的一般解决方案 std::lock()函数模板 std::lo ...

  10. .NET 6 Preview 3 发布

    前言 2021/4/8 .NET 6 Preview 3 发布,这个版本的改进大多来自于底层,一起来看看都有什么新特性和改进吧. 库改进 新增值类型作为字典值时更快的处理方法 .NET 6 Previ ...