测试与优化

测试与优化

学号1:211606367 姓名:林恩 学号2:211606445 姓名:肖志豪

一、预估与实际

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 30
• Estimate • 估计这个任务需要多少时间 30 30
Development 开发 690 650
• Analysis • 需求分析 (包括学习新技术) 70 50
• Design Spec • 生成设计文档 50 70
• Design Review • 设计复审 20 30
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 30 20
• Design • 具体设计 185 185
• Coding • 具体编码 185 185
• Code Review • 代码复审 30 30
• Test • 测试(自我测试,修改代码,提交修改) 120 120
Reporting 报告 60 50
• Test Repor • 测试报告 30 30
• Size Measurement • 计算工作量 10 10
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 10 20
合计 780 730

二、需求分析

需要了解Junit如何使用以及相关要求:

测试要求

  • 测试方法上必须使用@Test进行修饰
  • 测试方法必须使用public void进行修饰,不能带任何参数
  • 新建一个源代码目录来存放我们的测试代码
  • 测试类的包应该和被测试类保持一致
  • 测试单元中的每个方法必须可以独立测试,测试方法间不能有任何的依赖
  • 测试类使用Test作为类名的后缀(不是必须的)
  • 测试方法使用test作为方法名的前缀(不是必须的)

测试失败的两种情况

  • Failure一般由单元测试使用的断言方法判断失败所引起的,这经表示测试点发现了问题,就是说程序输出的结果和我们预期的不一样
  • error是由代码异常引起的,他可以产生于测试代码本身的错误,也可以是被测试代码中的一个隐藏的bug
  • 测试用例不是用来证明你是对的,而是用来证明你没有错;即测试用例不能证明你逻辑上的错误,只能检测出你代码中的错误

执行顺序

@BeforeClass - @Before - @Test - @After - @AfterClass

@BeforeClass:它会在所有方法运行前被执行,static修饰

@Before:会在每一个测试方法被运行前执行一次

@Test:将一个普通的方法修饰成为一个测试方法

xx.class为捕获异常类

@Test(expected = xx.class)

@Test(timeout = 毫秒)

@Ignore:所修饰的测试方法会被测试运行器忽略

@RunWith:可以更改测试运行器 org.junit.runner.Runner

@After:会在每一个测试方法运行后被执行一次

@AfterClass:它会在所有方法运行结束后被执行,static修饰

  • @BeforeClass修饰的方法会在所有方法被调用前被执行
  • 而且该方法是静态的,所以当测试类被加载后接着就会运行它
  • 而且在内存中它只会存在一份实例,他比较适合加载配置文件
  • @AfterClass所修饰的方法通常用来对资源的清理,如关闭数据库的连接
  • @Before@After会在每个测试方法的前后各执行一次
  • @Before@After会在每个测试方法的前后各执行一次
  • @Before@After会在每个测试方法的前后各执行一次

测试套件(批量执行测试用例)

测试套件就是组织测试类一起运行的

写一个作为测试套件的入口类,这个类里不包含其他的方法

更改测试运行suit.class

将要测试的类作为数组传入Suite.SuiteClasses({})

@RunWith(Suite.class)

@Suite.SuiteClasses({TaskTest1.class,TaskTest2.class,TaskTest3.class})

Junit参数化测试

更改默认的测试运行器为RunWith(Parameterized.class)

声明变量来存放预期值和结果值

声明一个返回值为Collection的公共静态方法,并使用@Parameters进行修饰

为测试类声明一个带有参数的公共构造函数,并在其中为之声明变量赋值

三、设计

1. 设计思路

说明你如何设计这个程序

设计单元测试的思路

  • assertEquals()和assertRaises(),比对方法返回值和逻辑值的差别,来检验方法的是否执行有效成功
  • 若是有的方法没有返回值,则先实例化,然后比对方法中的值和逻辑值的差别也是可以的
  • 有些需要赋予某些其它的初始值

单元测试

  • 部分单元测试代码

请展示一段程序的关键代码,并解释代码的作用

    @Test
	public void testMain() {
		String[] args = {"-n", "100", "-grade", "3"};
		MathExam math = new MathExam();
		math.main(args);
		assertEquals(true, math.output_boolean);
	}

测试方法上有@Test才会进行测试运算。先实例化,然后传参,math.output_boolean初值为false,执行完代码后math.output_boolean会赋值为true,代表执行完成。这时候只需要判断下值是否为true,即可代表main()是否执行成功。

要对各种可能的值进行测试,确保能够实现方法的各种可能

  • 单元测试覆盖率截图

测试与优化 单元测试覆盖率

结构优化

  • UML类图:
测试与优化 类图
  • 程序运行流程图
测试与优化 流程图

程序做出重构的部分,与重构的原因

  • 重构了关于两个数字的计算与判断,在原先代码里关于数字的计算判断大量重复,所以我将它生成为一个方法类与生成式子的类合并
  • 将生成式子的类从原先类中提取出,专门生成个类来生成式子以及判断,为了让每个类的模块与功能都更加精准、

重构后每个模块的功能

  • MathExam类(主类,自身负责生成和写入文件,以及调用其它方法类来生成式子)
  • Two_Numbers类(负责生成一年级和二年级式子以及子式判重,防止生成重复的式子包括子式,在生成三年级式子时调用Calculation类)
  • Calculation类(负责三年级式子,转成中缀表达式以及后缀表达式求和)

性能调优

  • 贴出优化前效能分析工具的结果截图
测试与优化 性能调优前
  • 描述程序的性能瓶颈

    • 在代码中存在着大量的重复代码,有些代码冗余无法行之有效实现功能
  • 给出优化方案

    • 进行了一定量的代码缩减提取,删减某些无用或者重复的代码
  • 贴出优化后效能分析工具的结果截图

测试与优化 性能调优后

2. 实现方案 写出具体实现的步骤

分为三个步骤:

单元测试

  • 针对程序架构,进行单元测试,确保每个模块能够达到预期的要求

结构优化

  • 把具有相似功能的代码封装在一起
  • 思考代码应该怎么进行结构优化
  • 每当代码的一个模块完成了重构,就需要commit
  • 为了保证每次重构后代码的正确性,我们需要先进行回归测试,再commit代码

性能调优

  • 测试并改进程序生成四则运算算式的效率
  • 帮我们分析找到代码中执行效率最差,也就是所谓 效能瓶颈 的部分。就可以把精力花费在改进瓶颈上,从而高效快速地提升程序性能

四、编码

调试日志

记录编码调试的日志,请记录下开发过程中的 debug 历程
比如:

  • 在代码的什么位置,代码出现了什么问题,问题会导致什么结果,怎么解决的

    • 在调试代码时,一旦更改代码,也需要在测试代码处进行及时的相对应更改
    • 对参数的传递方式,有两种,值传递和引用传递,在方法传递的参数中是值传递,更改子方法中的的值是不会修改到父方法相对应的值
  • 请说明你如何按照设计思路进行编码,并记录你在开发中遇到的问题,与解决过程

    • 在写测试时,查找到有两种方法assertEquals()和assertRaises()。了解到这两种方法各自作用,通过比较object判断不同来返回测试结果,如果相同,测试通过;反之,不通过。这对方法的模块化程度有着一定的要求。后来我想了些方法,不必通过一定要有方法返回值来判断。因为实现方法后,方法内的某些参数必然改变,这时候只要检验到这个方法的应该实现的内容达到逻辑上的值即可。
    • 需要对代码的命名,意义,规范有着很深的认识,毕竟不仅仅是自己一个人在编程,需要让其他人看懂读懂,了解代码。也方便自己后期阅读,修改,重构。

五、总结

  • 关于计算机题目重复特点,我总结了几个:
    • 题目完全相同
    • 题目不同,在顺序上不同,式子不同,但是子式相同,只是改变了形式。其实实质是相同的,结果相同
    • 比如a + b和b +a,a x b和b x a,
    • 最终决定在生成式子时,加上一个采集子式的集合用来判断子式是否重复