java 板


LINE

我跟我的厂商说 来导入TDD在这个专案吧 并且跟他们说 一边写SPEC 一边就可以写测试案例啦 他们总是跟我回应这不适合这个专案 对於她们来说测试是最後写的 并且总是可以找到理由证明测试是不切实际的 我才发现书上写的还真有道理 (我忘记是重构还是敏捷还是clean code了) 当我展示我的web service测试案例的时候 并且展示如何用TDD去修正专案MVC及DAO架构的时候 我才发现环境建立的重要性 对於测试,我认为有 1.针对需求建立的测试,主要是SPEC 2.检测系统元件架构的模组化程度,如果有某元件无法测试, 就表示架构有问题,而非测试不适合(哀~这就是厂商跟我说的不适合阿) 因此根据着眼点的不同,要建立的环境也不太一样 以我的web service为例 测试的范围是该服务的全部,并且要是黑盒子测试 因此每个测试基本上只有三行(其实不止拉) 1.建立资料 2.执行某服务 3.检查结果 public void test_helloworld(){ Data data = loadHelloworldData(); webservice.hellowWorld(data); Assert.assertEquals("Hello World",data.getRespose()); } 接着根据你的需求分别定义 例如我的测试资料都是以资料库为导向 所以我就是 public void test_helloworld(){ String id = "helloworld"; loadSQL(id+".sql"); Data data = instanceData(id); webservice.hellowWorld(data); Assert.assertEquals("Hello World" ,queryforOne("select message from xxx where id=?",id)); } 通常SQL也要抽离,但这是我的习惯啦 XD 因为测试有的时候是很赤裸裸的啦 因为测试环境的不良 往往导致测试案例不好撰写 1.例如测试资料要如何建立呢? 2.而且有些元件竟然很难测试 在此我的感想就是 1.建立良好的环境供测试 2.如果你无法测试某个元件,一定是你的元件有问题,而非不适合测试 所以可以倒过来说,如果对於某物件架构感到疑惑,那麽为该架构写测试案例 如果发现没办法好好写,那就是架构有问题, 同时也可以从写测试案例中,获得架构的灵感喔 因此测试并非是在专案开发好才做,是可以先做或是一边做都非常适合喔 所以大家一起来TDD吧 ※ 引述《qrtt1 (有些事,有时候。。。)》之铭言: : 此文是讨论这篇提问的案例: : ┌─────────────────────────────────────┐ : │ 文章代码(AID): #1Hj2uDh_ (java) [ptt.cc] [问题] 请问关於Junit Test Code? │ : │ 文章网址: http://webptt.com/cn.aspx?n=bbs/java/M.1370762765.A.AFF.html: │ 这一篇文章值 229 Ptt币 │ : └─────────────────────────────────────┘ : 由於上一篇的 Unit Test 实在长得不好看, : 加上它被锁文了就不针对他的问题去回应。 : 另外程式码长得不好看,看了只会想谯人。 : 回到那个问题的本质是或一组管理学生分数的程式, : 它的测试重点在於分数经过修改後仍然是符合逻辑的。 : 至於符合逻辑的细节是什麽? : 1. 符合 spec : 2. 符合常理(虽然这点偶尔跟 1. 冲突) : 我说那个 code 很丑是因为测试程试还要去搞定 user input 的东西, : 要去弄 System.in System.out,有太多跟 business logic 本身无关的东西。 : 由於没看过完整的需求,由原先的 test case 试着还原设计。 : 以下是我的设想。 : /* GradeSystem 长成这个样子,可以帮你找出某个 ID 的分数 */ : public class GradeSystem { : Map<String, Grade> grades = new HashMap<String, Grade>(); : public Grade findGradeByID(String userId) { : Grade grade = grades.get(userId); : if (grade == null) { : throw new NoSuchIDExceptions(); : } : return grade; : } : public void setGrade(String userId, Grade grade) { : grades.put(userId, grade); : } : } : /* Grade 代表分数,它同时知道它代表哪一个 ID 与名称 */ : public class Grade { : String id; : String name; : Vector<GradeItem> items = new Vector<GradeItem>(); : public void setId(String id) { : this.id = id; : } : public void setName(String name) { : this.name = name; : } : public void newItem(String name, int score) { : GradeItem item = new GradeItem(); : item.setName(name); : item.setScore(score); : items.add(item); : } : public GradeItem getItem(String name) { : for (GradeItem item : items) { : if (item.getName() != null && item.getName().equals(name)) { : return item; : } : } : return null; : } : public int sum() { : int sum = 0; : for (GradeItem item : items) { : sum += item.getScore(); : } : return sum; : } : } : /* 由於要计分几次未定,在 Grade 内另外用 GradeItem 记录实际的分数 */ : public class GradeItem { : String name; : int score; : public String getName() { : return name; : } : public void setName(String name) { : this.name = name; : } : public int getScore() { : return score; : } : public void setScore(int score) { : this.score = score; : } : } : /* 一个基本的测程式,它目前只完成了初始资料设定 */ : public class GradeSystemTest { : private static final String USER_NAME = "凌宗廷"; : private static final String USER_ID = "962001044"; : Random random = new Random(); : GradeSystem gradeSystem; : String[] gradeItmeNames = { "Lab1", "Lab2", "Lab3", : "Mid-Term", "Final exam" }; : int totalScore = 0; : @Before : public void setUp() { : gradeSystem = new GradeSystem(); : prepareTestData(); : } : private void prepareTestData() { : Grade grade = new Grade(); : grade.setId(USER_ID); : grade.setName(USER_NAME); : for (String itemName : gradeItmeNames) { : int score = anyScore(); : totalScore += score; : grade.newItem(itemName, score); : } : assertEquals(grade.sum(), totalScore); : gradeSystem.setGrade(USER_ID, grade); : } : @Test : public void testChangeScore() { : /* 在这里我们要测试程式逻辑对不对 */ : } : protected int anyScore() { : int score = random.nextInt(200); : if (score >= 0 && score <= 100) { : return score; : } : return anyScore(); : } : } : ====================================================================== : 关於测试资料,尽可能使用随机产生的。 : 除了一些边界条件的 CASE 都用随机的! : 所以在初始分数的设定,我们用随机的分数,只要它是 0~100 就行了。 : 更理想是连 item names 都随机(产生出 1 笔以上的 item) : private void prepareTestData() { : Grade grade = new Grade(); : grade.setId(USER_ID); : grade.setName(USER_NAME); : for (String itemName : gradeItmeNames) { : int score = anyScore(); : totalScore += score; : grade.newItem(itemName, score); : } : assertEquals(grade.sum(), totalScore); : gradeSystem.setGrade(USER_ID, grade); : } : 在准备资料的过程,我们已经有第 1 个验证: : assertEquals(grade.sum(), totalScore); : 测试分数有没有改好的整套逻辑是,各别分数要对,总分也要对。 : 因此我们验证它的时候,不是只有验证单一项目的分数 : 还有综合起来的结果是不是对的,更符合实际的情况应该要有权重配分。 : 那麽回到主要的「改变分数」的逻辑验证: : @Test : public void testChangeScore() { : } : 要改变分数我们必需要: : 1. 找到改变的项目 : 2. 重新设定该项目的分数 : 改变完成後,我们需 : 3. 取出该项目的分数是否符合预期 : @Test : public void testChangeScore() { : Grade grade = gradeSystem.findGradeByID(USER_ID); : for (String itemName : gradeItmeNames) { : GradeItem item = grade.getItem(itemName); // 1. : int oldScore = item.getScore(); : int newScore = anyScore(); : item.setScore(newScore); // 2. : assertEquals(item.getScore(), newScore); // 3. : } : } : 单项的验证虽然做了,但它仍是不足的。 : 做 Unit Test 不是让你单纯测试你的 getter & setter 有没有正常运作 : 改变单一项变的分数会影响最终的结果,这个结果的改变需要符合期待 : 这才是 Unit Test 要做的工作: : 「验证物件随着状态改变後,行为仍符合预期」 : 那我们该多期待什麽?对一个算分系统来说,至少你改单项总分要对。 : (电视台那个选票系统常常教坏别人,常弄得单项加起来不是总分XD) : 那麽总分该怎麽测试它呢?在你设定新的分数前,你可以用新的分数预测出总分 : 所以我们会这麽写: : @Test : public void testChangeScore() { : Grade grade = gradeSystem.findGradeByID(USER_ID); : for (String itemName : gradeItmeNames) { : GradeItem item = grade.getItem(itemName); : int oldScore = item.getScore(); : int newScore = anyScore(); : item.setScore(newScore); : assertEquals(item.getScore(), newScore); : int newTotal = totalScore + (newScore - oldScore); : assertEquals(grade.sum(), newTotal); : totalScore = newTotal; : } : } : 这样子的设计与验证不是简单又易懂吗? : 要如何证名简单又易懂?(思)... : 大概只能说,我注解比你少,但多数人应该觉得我的 code 比你好看!? -- --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 163.29.29.131
1F:→ qrtt1:不知为什想起这了个 http://bit.ly/195Xbkt 06/13 18:00







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Soft_Job站内搜寻

TOP