2016.06.24-2016.06.30这周工作时间和内容

这周学习内容:马上要考试了,这周老师给我们复习了判定树和判定表。首先老师先出了一道题让画出判定表,两种画法都可以,我画完之后,老师用的在班里讲,老师指出了我画的问题,包括我对折扣的换算的错误,还有我对结果的表达方式的错误。很感谢老师可以用我画的图来讲解,让我更深刻的加深印象,以后像这样的错误也不会发生,非常的感谢。这个学期马上要结束了,老师辛苦了,感谢您!

这周的阅读内容:

《构建之法》第二章 个人技术和流程

概述

一个团队需要一定的流程来管理开发活动,每个工程师在软件生命周期所做的工作也应该有一个流程,这一章会介绍PSP(Personal Software Pro-cess,个人软件开发流程)

单元测试的作用:让自己负责的模块功能定义尽量明确,模块内部的改变不会影响其他模块,而且模块的质量能得到稳定的、量化的保证

单元测试


用VSTS写单元测试

例子:许多应用程序中都会用到“用户”这一类型,用户的标识通常是一个邮件地址。对应的单元测试该怎么写?我们来练习一下。首先创建了一个C#的类库(Class Library),并写了如代码清单2-1所示的代码

代码清单2-1

namespace DemoUser
{
    public class User
    {
        public User(string userEmail)
        {
            m_email = userEmail;
        }

        //user email as user id
        private string m_email;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在单元测试中,VSTS自动为你生成了测试的骨架,但是你还是要自己做不少事情,最起码要把那些标注为//TODO的事情给做了(如代码清单2-2所示)。此时,单元测试还在使用Assert.In-conclusive,表明这是一个未经验证的(Inconclu-sive)单元测试

代码清单2-2

/// <summary>
/// A test for User (string)
/// </summary>
[TestMethod()]
public void ConstructorTest()
{
    // TODO: Initialize to an appropriate
    // value    User target = new User(userEmail);
    string userEmail = null; 

    // TODO: Implement code to verify target
    Assert.Inconclusive("TODO: Implement code to verify target");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

简单修改之后,可以得到一个正式的单元测试,如代码清单2-3所示。 
代码清单2-3

[TestMethod()]
public void ConstructorTest()
{
    string userEmail = "[email protected]";
    User target = new User(userEmail);
    Assert.IsTrue(target != null);
}
//我们还可以进一步测试E-mail是否确实保存在User类型中
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

从上面这个例子可以看到,创建单元测试函数的主要步骤是:

1. 设置数据(一个假想的正确的E-mail地址) 
2. 使用被测试类型的功能(用E-mail地址来创建一个User类的实体) 
3. 比较实际结果和预期的结果(Assert.IsTrue(target != null);)

现在可以运行单元测试了,同时可以看看代码覆盖报告(Code Coverage Report),代码百分之百地都被覆盖了。当然这时候的代码还有很多情况没有处理。例如,还没有:处理空的字符串,长度为零的字符串,都是空格的串……我们可以很快地复制/粘贴,又写了下面三个测试,如代码清单2-4所示。

代码清单2-4

[TestMethod()]
[ExpectedException(typeof(ArgumentNullException))]
public void ConstructorTestNull()
{
    User target = new User(null);
}

[TestMethod()]
[ExpectedException(typeof(ArgumentException))]
public void ConstructorTestEmpty()
{
    User target = new User("");
}

[TestMethod()]
[ExpectedException(typeof(ArgumentNullException))]
public void ConstructorTestBlank()
{
    User target = new User("     ");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

如果不修改类库中的代码,单元测试会报告这三个新的测试都失败了

写技术模块的规格说明书(Specification)的时候,要越详细越好,最好各项要求都可以表示为一个单元测试用例


好的单元测试的标准

单元测试应该准确、快速地保证程序基本模块的正确性。下面是验证单元测试好坏的一系列标准: 
1. 单元测试应该在最基本的功能/参数上验证程序的正确性 
单元测试应该测试程序中最基本的单元—如在C++/C#/Java中的类,在此基础上,可以测试一些系统中最基本的功能点(这些功能点由几个基本类组成)。从面向对象的设计原理出发,系统中最基本的功能点也应该由一个类及其方法来表现。单元测试要测试API中的每一个方法及每一个参数

2. 单元测试必须由最熟悉代码的人(程序的作者)来写 
代码的作者最了解代码的目的、特点和实现的局限性。所以,写单元测试没有比作者更适合的人选了。最好是在设计的时候就写好单元测试,这样单元测试就能体现API的语义,如果没有单元测试,语义的准确性就不能得到保障,以后会产生歧义

3. 单元测试过后,机器状态保持不变 
这样就可以不断地运行单元测试,如果单元测试创建了临时的文件或目录,应该在Teardown阶段删掉。如果单元测试在数据库中创建或修改了记录,那么也许要删除或恢复这些记录,或者每一个单元测试使用一个新的数据库,这样可以保证单元测试不受以前单元测试实例的干扰

4. 单元测试要快(一个测试的运行时间是几秒钟,而不是几分钟) 
快,才能保证效率。因为一个软件中有几十个基本模块(类),每个模块又有几个方法,基本上我们要求一个类的测试要在几秒钟内完成。如果软件有相互独立的几个层次,那么在测试组中可以分类,如数据库层次、网络通信层次、客户逻辑层次和用户界面层次,可以分类运行测试,比如只修改了“用户界面”的代码,则只需运行“用户界面”的单元测试

5. 单元测试应该产生可重复、一致的结果 
如果单元测试的结果是错的,那一定是程序出了问题,而且这个错误一定是可以重复的,单元测试不能解决所有问题,不必期望它会发现所有的缺陷

6. 独立性—单元测试的运行/通过/失败不依赖于别的测试,可以人为构造数据,以保持单元测试的独立性 
程序中的各个模块都是互相依赖的,否则它们就不会出现在一个程序中。一般情况下,单元测试中的模块可以直接引用其他的模块,并期待其他的模块能返回正确的结果。如果其他的模块很不稳定,或者其他模块运行比较费时(如进行网络操作),而且对于本模块的正确性并不起关键的作用,这时可以人为地构造数据,以保证单元测试的独立性

7. 单元测试应该覆盖所有代码路径 
单元测试应覆盖所测单元的所有代码路径,包括错误处理路径。为了保证代码覆盖率,单元测试必须测试公开的和私有的函数/方法 
100%的代码覆盖率并不等同于100%的正确性!

  • 代码覆盖率对于“应该写但是没有写的代码”无能为力。例如代码申请了内存或其他资源,但并没有释放。又如,代码中并没有处理错误情况。就像没有处理和文件、网络相关的一些异常情况,例如文件不存在、权限有问题,等等
  • 代码中有效能问题,虽然代码执行了,并且也正确地返回了,但是代码效率非常低。有些情况下,可以针对代码效率写一个单元测试
  • 多线程环境中的同步问题,这个问题和代码执行的时序、共享资源的锁定有关
  • 其他与外部条件相关的问题(例如与设备、网络相关的问题)

8. 单元测试应该集成到自动测试的框架中 
要把单元测试自动化,这样每个人都能随时、随地运行单元测试。团队一般是在每日构建之后运行单元测试的,这样单元测试的错误就能及时被发现并得到修改

9. 单元测试必须和产品代码一起保存和维护 
单元测试必须和代码一起进行版本维护。如果不是这样,过了一阵,代码和单元测试就会出现不一致,程序员要花时间来确认哪些是程序出现的错误,哪些是由于单元测试滞后造成的错误


回归测试

在单元测试的基础上,我们就能够建立关于这一模块的回归测试(Regression Test)。Regress 的英语定义是:return to a worse or less developed state,是倒退、退化、退步的意思。在软件项目中,如果一个模块或功能以前是正常工作的,但是在一个新的构建中出了问题,那么这个模块就出现了一个“退步”(Regression),从正常工作的稳定状态退化到不正常工作的不稳定状态。在一个模块的功能逐步完成的同时,与此功能有关的测试用例也同样在完善中。一旦有关的测试用例通过,我们就得到了此模块的功能基准线(Baseline),一个模块的所有单元测试就是这个模块最初的Baseline。假如,在3.1.5版本,模块A的编号为125的测试用例是通过了的,但是在新的版本3.1.6上,这个测试用例却失败了,这就是一个“倒退”(Regres-sion)。工程师们应该在新版本上运行所有已通过的测试用例,以验证有没有“退化”情况发生,这个过程就是一个“Regression Test”。如果这样的“倒退”是由于模块的功能发生了正常变化引起的(例如,我们要修改模块,支持电子邮件地址以.name为最后的域名),那么测试用例的基准就要修改,以便和新的功能保持一致。针对一个Bug Fix,我们也要做Regression Test。目的是:

1. 验证新的代码的确改正了缺陷 
2. 同时要验证新的代码有没有破坏模块的现有功能,有没有Regression 
所以,对于“回归测试”中的“回归”,我们可以将其理解为“回归到以前不正常的状态”。回归测试最好要自动化,因为这样就可以对于每一个构建快速运行所有回归测试,以保证尽早发现问题。单元测试是回归测试的基础。在专注于模块基本功能的单元测试之外,还有功能测试—从用户的角度检查功能完成得怎么样。在微软的实践中,在一个项目的最后稳定阶段,所有人都要参加全面的测试工作,把所有以前发现并修复的Bug找出来,一个一个验证,以保证所有已经修复过的Bug的确得到了修复,并且没有在最后一个版本中“复发”,这是一个大规模的、全面的“回归测试”


效能分析工具

效能分析,Performance!这是每一个程序员都梦想的事儿,让自己的程序跑得又快又好,最好是比别人快一个数量级,别人的程序是O(N^2),而我的程序是O(n×logN),或者是O(N),这是多爽的一项成就呀!VSTS提供了方便的效能分析工具,让我们能很快地找到程序的效能瓶颈,从而能有的放矢,改进程序。下面我们看一个具体的例子。有这样一道题:写一个程序,分析一个文本文件中各个词出现的频率,并且把出现频率最高的10个词打印出来,程序的基本框架如下面代码所示

DoIt()
{
    //store all words in a big buffer
    ProcessFile()
    //calculate and store the frequency of each word
    ProcessBuffer()
    //output top10
    OutputResult()
}
ProcessBuffer()
{
    //get one word from buffer
    GetOneWord()
    FreqOneWord()
}
FreqOneWord(word)
{
    Find the word in the array list,
    If (found)
        Update the frequency
    If (not found)
        Add the word in the array list with frequency =1
}
OutputResult()
{
    //sort the array
    ArrayList.Sort()
    Output Top10 entry;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

1. 实践的第一步,要确保编译的程序是Release版本

2. 选择两种分析方法

  • 抽样(Sampling) 
    简单来说,抽样就是当程序运行时,Visual Studio时不时看一看这个程序运行在哪一个函数内,并记录下来。程序结束后,Visual Studio就会得出一个关于程序运行时间分布的大致印象。这种方法的优点是不需要改动程序,运行较快,可以很快找到瓶颈,但是不能得出精确的数据,也不能准确表示代码中的调用关系树(Call Tree)
  • 代码注入(Instrumentation) 
    另一方面,代码注入就是将检测的代码加入到每一个函数中,这样程序的一举一动都被记录在案,程序的各个效能数据都可以被精准地测量。这一方法的缺点是程序的运行时间会大大加长,还会产生很大的数据文件,也相应增加了数据分析的时间。同时,注入的代码也影响了程序真实的运行情况(这有点像量子物理学中的“测试的光线干扰了被测物体本身”的现象)。

一般的做法是,先用抽样的方法找到效能瓶颈所在,然后对特定的模块用代码注入的方法进行详细分析。对程序进行效能分析,我们先要弄清楚下面这几个名词,如下表所示

名词 含义
调用者 (Caller) 函数 Foo() 中调用 Bar(),Foo() 就是调用者
被调用者函数(Callee) 见上,Bar() 就是被调用者函数
调用关系树 (Call Tree) 从程序的 Main() 函数开始,调用者和被调用函数就形成了一个树形关系——调用树
消逝时间(Elapsed Time) 从用户的角度来看程序运行所花的时间。当用户看到一个程序没有反应时,用户并不清楚程序 此时时在运行自己的代码,还是被调度出去了,或者操作系统此时正在忙别的事情
应用程序时间(Application Time) 应用程序占用 CPU 的时间,不包括 CPU 在核心时态花费的时间
本函数时间(Exclusive Time) 所有在本函数花费时间,不包括被调用者使用的时间
所有时间(Inclusive) 包含本函数和所有调用使用的时间

理解了上面的各种概念后,就不难理解“消逝的本函数时间(Elapsed Exclusive Time)”等其他组合名词所代表的概念了。我们先进行抽样分析,在效能浏览器(PerformanceExplorer)中开始效能分析即可。耗时最多的三个函数是:

WordFreq.Freq.FreqOneWord(string)
System.String.EqualsHelper(string,string)
System.Collections.ArrayList.get_Item(int32)
  • 1
  • 2
  • 3

三个函数加起来占用了整个程序84%的时间。看来我们得分析为什么这三个函数会被调用得这么频繁,开销这么大了。现在可以进行代码注入的分析,同样运行程序后,看看调用关系树(Call Tree)报告,结合实际的代码(见下列代码),可以看到在WordFreq.FreqOneWord函数中,究竟发生了什么

private void FreqOneWord(string w)
{
    // see if we have a match, if not, add it to the end,
    // then assign it initial frequency1;
    // if yes, inc the frequency by1
    for (int i =0; i < m_wordList.Count; i++)
    {
        Frequency fi = (Frequency)m_wordList[i];
        if (fi.str == w)
        {
            fi.n++;
            return;
        }
    }
    //now we have to append it to the end.
    Frequency f = new Frequency();
    f.str = w;
    f.n =1;
    m_wordList.Add(f);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

以下代码花费时间太多了

for (int i =0; i < m_wordList.Count; i++)
  • 1

可以马上把代码改成:

int count = m_wordList.Count;
for (int i =0; i < count; i++)
  • 1
  • 2

可以看到System.Collections.ArrayList.get_Count()的调用次数和时间都大幅减少。可以继续进行“效能测试,分析,改进,再效能测试”的流程,逐渐提高程序的效能和我们的编程水平。大家也要注意避免没有做分析就过早地进行“效能提高”,如果我们不经分析就盲目优化,也许会事倍功半


 

个人开发流程

卡内基梅隆大学(CMU)的能力成熟度模型(CMM和CMMI),是用来衡量一个团队能力的一套模型。CMU的专家们针对软件工程师也有一套模型,叫Personal Software Process(PSP),PSP和任何其他方法论一样,也不是一蹴而就的 
个人项目耗时记录表

PSP2.1 PSP Stage Time(%) SDE
Planning 计划 6%
Estimate 估计这个任务需要多少时间 6
Development 开发 88%
Analysis 需求分析 10
Design Spec 生成设计文档 6
Design Review 设计复审(和同事审核设计文档) 6
Coding Standard 代码规范(为目前的开发制定合适的规范) 3
Design 具体设计 12
Coding 具体编码 21
Code Review 代码复审 9
Test 测试(自测,修改代码,提交修改) 21
Reporting 报告 6%
Test Report 测试报告 2
Size Measurement 计算工作量 1
Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 3

PSP有如下的特点:

    • 不局限于某一种软件技术(如编程语言),而是着眼于软件开发的流程,这样,开发不同应用的软件工程师可以互相比较
    • 不依赖于考试,而主要靠工程师自己收集数据,然后分析,提高
    • 在小型、初创的团队中,很难找到高质量的项目需求,这意味着给程序员的输入质量不高。在这种情况下,程序员的输出(程序/软件)往往质量也不高,然而这并不能全部由程序员负责
时间: 2024-10-13 03:25:25

2016.06.24-2016.06.30这周工作时间和内容的相关文章

2016.06.10-2016.06.16这周工作时间和内容

由于这周是端午节,我们放假了,就没有上课. 这周的阅读内容: 第1章 概论 理论和知识点 计算机科学的领域 软件的特性 软件工程 软件工程与 计算机科学的关系 练习与讨论 练习不断通过简单的程序来实现一系列要求和改进 从切身体会出发,讨论软件的特性,软件和 软件工程对社会的影响 结合所在的学校的特点和自身经历,讨论 软件工程与 计算机科学的区别 1.1 软件 = 程序 + 软件工程(1) 几乎所有的程序员都知道“程序= 数据结构+算法”1 这句名言,但是在实际的学习和工作中,也有不少人产生了疑问

2016.05.26-2016.06.02这周工作时间和内容

这周学习的内容:这周和平常是一样的,我们在课堂上学习了三个小时,老师讲了用例图,类图,序列图,状态图.我们了解了用例图,用例图(use case diagram)就是由主角.用例以及它们之间的关系构成的图.该图说明了用例模型中的关系.类图(Class diagram)由许多(静态)说明性的模型 元素(例如类.包和它们之间的关系,这些元素和它们的内容互相连接)组成.类图可以组织在(并且属于)包中,仅显示特定包中的相关内容.类图(Class diagram)是最常用的 UML图,显示出类.接口以及它

2016.06.03-2016.06.09这周工作时间和内容

这周的学习内容,这周是我们实训任务很大,老师让做一个大的程序,功能要齐全,我是李志芳组的,我们组的题目是酒店客房管理系统,我们一起研究了功能,布置了我们每个人的分工,每个人的模块,我做的是员工信息管理,主要管理员工的信息,通过我们的努力,做完了报告,提交了,这周的实训就这样结束了,收获很大,主要是学会了画用例图,在调程序上,我又学会了很多,刚开始的时候数据库就是连不成功,后来通过上网查资料,我研究会了怎么去修改数据库字符串,真的有很大的收获,感谢这次实训,感谢老师. 这周的阅读内容:“大数据”是

2016.05.20-2016.05.26这周工作时间和内容

这周的学习内容:这周学习了NS图和PAD图,流程图由一些特定意义的图形.流程线及简要的文字说明构成,它能清晰明确地表示程序的运行过程.在使用过程中,人们发现流程线不一定是必需的,为此,人们设计了一种新的流程图,它把整个程序写在一个大框图内,这个大框图由若干个小的基本框图构成,这种流程图简称N-S图.NS图形象直观,具有良好的可见度.例如循环的范围.条件语句的范围都是一目了然的,所以容易理解设计意图,为编程.复查.选择测试用例.维护都带来了方便:NS图简单.易学易用,可用于软件教育和其他方面:功能

2016.03.10-2016.03.17这周工作时间和内容

这周的学习内容:我给自己每周定的是学习3个半小时,我在课堂上认真的学习了三个小时,回到宿舍也学习了半个小时.在这周三个小时的课堂上老师让我自己去编写四则运算,刚开始是用户输入两个数和一个运算符,求出结果,就是简单的加减乘除,刚开始编这个代码的时候,我是左思右想,怎么也想不出来,后来从网上看到了别人类似的代码,我把它敲进去,然后运行不出来,我经过自己的调试,和慢慢的琢磨终于成功了,接着老师让输入三个数和俩个运算符求出结果,我这次就学会了,我自己对代码进行了改编,自己对代码进行了理解和加深,终于把之

2016.05.06-2016.05.12这周工作时间和内容

这周学习内容:这周 也没有学习什么新的东西,因为有全国的技能大赛在我们轻工举行,我们机房的电脑很多都被拿去供参赛的选手用,我们上课因为没有机器,同学们都是好几个人坐一个电脑,所以老师 没有讲课,复习了我们之前学习的东西,复习了ER图,数据流图和定义数据,我对于这个数据流图还是不太懂,不过经过老师的复习,我对这个数据流图有了更深的理解,画起来也很容易了,每一次对学习的东西有一个复习是个很好事情,我要努力的慢慢的养成这个习惯. 这周的阅读内容:<代码大全(第2版)>是著名IT畅销书作者SteveM

2016.05.13-2016.05.19这周工作时间和内容

这周的学习内容:这周电脑有好多也不能用大家都是一起用的,我同样是学习了三个小时,外加回宿舍学习的一个小时,我在画图,画图中遇到了很多问题,我不知道该画箭头还是直线,也不知道该怎么去构想过程,老师这节课讲了软件结构图的画法以及它的用法,定义,我学会了怎么去构思和动手画.软件结构图(Structure Chart, 简称SC)是软件系统的模块层次结构,反映了整个系统的功能实现. 软件结构以层次表示程序的系统结构,即一种控制的层次体系,并不表示软件的具体过程. 件结构一般用树状或网状结构的图形来表示.

2016.04.29-2016.05.05这周工作时间和内容

这周的学习内容:这周我还和平常一样,上了三个小时的课,下课用一个小时来巩固自己的学习的知识,然后对自己学习的内容加以深化,其实我自己对于学习是懒的,懒的学,懒的动,每次上课虽然说不上特别的认真听,但是老师让学习的,让做的我也没有落下,估计是我自己对学习太过于懒惰了,很多东西就是不知道,也不明白,从第一节开始,自己的落下了,一直到现在,总有些东西是不明白,就像这周吧,这周课学习了ER图和状态图,我对于这个ER图就不是那么的熟悉了,之前的学期,学习过数据库等等,在交课程报告的时候也有做过ER图,可是

2016.04.8-2016.04.14这周工作时间和内容

这周的学习内容:这周学习是4个小时,除了上课以为,给自己多增加一个小时来学习,感觉很好,学习了很多,这周主要学习了团队形式1.窝蜂模式 (chaos team) ? 2.主治医师模式: (Chief-Programmer Team, surgical team) ? 3.明星模式 (Super-star model) ?4. 社区模式 (Community Model) ?5. 业余剧团模式 (Amateur Theater Team) 6. ?秘密团队 (skunk work team)?7.