软件工程慕课第三章编程题—对game_map.py进行单元测试
一、编程思路
1、采用unittest库对代码进行单元测试
2、用coverage库对测试代码进行覆盖率测试
二、代码实现
1、unittest
首先要读懂game_map.py这个文件,里面包含了地图相关的接口,每读一个方法我就会进行一些测试,简单调用可以帮助更好的理解代码,下面是我读代码的时候进行的一些尝试:
读完代码后就知道这些方法是做什么的了,然后就学习了unittest这个库怎么使用,参考了很多csdn上的博客,对使用有一个大概理解,有几个重要的语法:
assertEqual用来判断函数的输入值和返回值是否一致来对函数进行测试,assertRaises用来判断函数对不正确的输入是否能正确抛出异常,mock.patch用来模拟还未编写完成的函数返回值,或者模拟你需要返回特定值的函数,下面贴出代码
1 '''测试game_map.py''' 2 import unittest 3 4 from unittest import mock 5 6 from game_map import * 7 8 import HTMLTestRunner 9 10 from itertools import cycle 11 12 class TestGameMap(unittest.TestCase): 13 '''测试 GameMap''' 14 def setUp(self): 15 self.game_map = GameMap(5, 4) 16 17 def test_init(self): 18 self.assertRaises(TypeError, GameMap, (1.2,4)) 19 self.assertRaises(TypeError, GameMap, (4,1.3)) 20 21 def test_rows(self): 22 self.assertEqual(5,self.game_map.rows) 23 self.assertEqual(4,self.game_map.cols) 24 25 @mock.patch('random.random', new = mock.Mock(side_effect = cycle([0.4, 0.6]))) 26 def test_reset_get_set(self): 27 self.assertRaises(TypeError,self.game_map.reset,(4)) 28 self.game_map.reset() 29 self.assertEqual([[1,0,1,0],[1,0,1,0],[1,0,1,0],[1,0,1,0],[1,0,1,0]], self.game_map.cells) 30 self.assertEqual(1, self.game_map.get(0,0)) 31 self.assertRaises(TypeError,self.game_map.get,(1.2,2)) 32 self.assertRaises(TypeError,self.game_map.get,(2,1.2)) 33 self.game_map.set(0,0,0) 34 self.assertEqual(0, self.game_map.get(0,0)) 35 self.assertRaises(TypeError,self.game_map.set,(6,0,0)) 36 self.assertRaises(TypeError,self.game_map.set,(0,4,0)) 37 self.assertRaises(TypeError,self.game_map.set,(0,0,2)) 38 @mock.patch('random.random', new = mock.Mock(side_effect = cycle([0.4]))) 39 def test_get_neighbor_count_and_map(self): 40 self.game_map.reset() 41 for i in range(0,self.game_map.rows): 42 for j in range(0,self.game_map.cols): 43 self.assertEqual(8, self.game_map.get_neighbor_count(i,j)) 44 self.assertEqual([[8,8,8,8],[8,8,8,8],[8,8,8,8],[8,8,8,8],[8,8,8,8]], self.game_map.get_neighbor_count_map()) 45 self.assertRaises(TypeError,self.game_map.get_neighbor_count,(5,0)) 46 self.assertRaises(TypeError,self.game_map.get_neighbor_count,(0,5)) 47 48 def test_set_map(self): 49 self.game_map.set_map([[0,0,0,0],[1,1,1,1],[0,1,0,1],[1,0,1,0],[1,1,0,0]]) 50 self.assertEqual([[0,0,0,0],[1,1,1,1],[0,1,0,1],[1,0,1,0],[1,1,0,0]], self.game_map.cells) 51 self.assertRaises(TypeError,self.game_map.set_map,(5)) 52 self.assertRaises(AssertionError,self.game_map.set_map,([[0]*4]*4)) 53 self.assertRaises(TypeError,self.game_map.set_map,([0]*5)) 54 self.assertRaises(AssertionError,self.game_map.set_map,([[0]*3]*4)) 55 self.assertRaises(TypeError,self.game_map.set_map,([[0.1]*4]*5)) 56 self.assertRaises(AssertionError,self.game_map.set_map,([[2]*4]*5)) 57 58 def test_print_map(self): 59 self.assertRaises(TypeError,self.game_map.print_map,(1)) 60 self.assertRaises(TypeError,self.game_map.print_map,([1,2],1)) 61 self.game_map.print_map = mock.Mock() 62 self.game_map.print_map(['2']) 63 self.game_map.print_map.assert_called_with(['2']) 64 65 66 def main(): 67 filepath = 'F:/multisim/VsCode/软件工程学习/test_game_map_report.html' 68 ftp=open(filepath,'wb') 69 70 suite = unittest.makeSuite(TestGameMap) 71 72 # unittest.main(verbosity=2) 73 runner=HTMLTestRunner.HTMLTestRunner(stream=ftp,title='test_game_map') 74 runner.run(suite) 75 76 if __name__ == '__main__': 77 78 main() 79 80 81
每一个方法的测试用例涵盖了所有路径,能起到一个比较好的测试作用,最后用HTMLTestRunner生成html报告,可以在报告里看到测试情况:
2、coverage
用coverage可以测试代码的覆盖率,用来测试测试代码的话,可以看到测试文件和被测文件的覆盖情况,coverage有两种用法,一种是命令行,一种是API,两种用法都很简单方便:官方文档 https://coverage.readthedocs.io/en/latest/api.html#api,两种方法我都试了:
命令行操作起来稍微麻烦一些,要一次一次的输命令,下面是API的方式:
只需要在cov.start和cov.stop之间贴上需要测试的代码即可,这里有一个问题如果在unittest中采用的是unittest.main方法的话,这里不能正确导入测试用例,原因目前还不清楚,解决方法是采用其他的运行测试用例的方法,coverag还可以生成html报告:
非常简洁明了,测试文件调用的所有子文件也会进行覆盖率测试,点击进去可以看到每行代码的详细测试信息:
总共两个py文件,4个测试报告已经上传github:https://github.com/dbefb/nickYang/tree/master/GameMap单元测试、覆盖率测试