OK,现在我们正式开始junit4系列的整理。前面的junit38作为4的补充知道就好了,实际编码中我们以4为主。这里先来一把junit的初体验,同时也让我们来一步一步的了解下TDD的好处。
ORM大家肯定熟悉的不能再熟悉了,大Java面向对象编码,但是数据库存的是表结构,所以我们不可避免的就要来做ORM映射。其中关键的一步就是将Java对象映射成数据库中的表,将一个Java对象的属性映射成数据库一个表的一个字段。
在这里我们就写一个工具类,将Java对象名称按照数据库命名的习惯来进行格式化。假如我们现在还没有任何的测试,一般的TDD中都是测试先行,先写个测试方法,因为没有任何的测试源代码,所以这个时候测试方法中要fail出去一个异常。然后人工去实现测试然后接着测试。在这里我们就不用先写那个失败的测试方法了,我们这里直接编码一个方法来实现刚才那个功能。
代码如下:
public String underscoreName(String name) { StringBuilder result = new StringBuilder(); result.append(name.substring(0, 1).toLowerCase()); for (int i = 1; i < name.length(); i++) { String s = name.substring(i, i + 1); String slc = s.toLowerCase(); if (!s.equals(slc)) { result.append("_").append(slc); } else { result.append(s); } } return result.toString(); }OK,现在方法写好了,那我们开始写测试。
@Test public void testUnderScoreName4Normal() { Linkin linkin = new Linkin(); String underscoreName = linkin.underscoreName("userInfo"); assertEquals("user_info", underscoreName); }
OK,写的这个测试用例通过了,那是不是就万事大吉了呢?NO,NO,NO,现在就宣布代码通过了单元测试还为时过早。我们应该记住:单元测试代码不是用来证明我是对的,而是为了证明我没有错。 因此单元测试的范围要全面,比如对边界值、正常值、错误值得测试;对代码可能出现的问题要全面预测,而这也正是需求分析、详细设计环节中要考虑的。显然, 我们的测试才刚刚开始,继续补充一些对特殊情况的测试。
OK,现在我们来写相关测试:
package test.junit4test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import org.junit.Test; public class LinkinTest { @Test // 测试字符串正常的情况 public void testUnderScoreName4Normal() { Linkin linkin = new Linkin(); String underscoreName = linkin.underscoreName("userInfo"); assertEquals("user_info", underscoreName); } @Test // 测试字符串为null的情况 public void testUnderScoreName4Null() { Linkin linkin = new Linkin(); String underscoreName = linkin.underscoreName(null); assertNull(underscoreName); } @Test // 测试字符串为空字符串的情况 public void testUnderScoreName4Empty() { Linkin linkin = new Linkin(); String underscoreName = linkin.underscoreName(""); assertEquals("", underscoreName); } @Test // 测试当首字母大写时的情况 public void testUnderScoreName4Begin() { Linkin linkin = new Linkin(); String underscoreName = linkin.underscoreName("UserInfo"); assertEquals("user_info", underscoreName); } @Test // 测试当尾字母为大写时的情况 public void testUnderScoreName4End() { Linkin linkin = new Linkin(); String underscoreName = linkin.underscoreName("userInfO"); assertEquals("user_info", underscoreName); } @Test // 测试多个相连字母大写时的情况 public void testUnderScoreName4Together() { Linkin linkin = new Linkin(); String underscoreName = linkin.underscoreName("userINfo"); assertEquals("user_info", underscoreName); } }测试代码写好以后,运行结果如下:
忽忽,蛋疼的情况出现了,2个错误,2个失败,好吧,说明我们的代码问题还是挺多的呢。开始改吧。
public String underscoreName(String name) { if (name == null) { return null; } if ("".equals(name)) { return ""; } StringBuilder result = new StringBuilder().append(name.substring(0, 1).toLowerCase()); for (int i = 1; i < name.length() - 1; i++) { String s = name.substring(i, i + 1); String slc = s.toLowerCase(); String pres = name.substring(i - 1, i); String preslc = pres.toLowerCase(); if (!s.equals(slc) && pres.equals(preslc)) { result.append("_").append(slc); } else { result.append(slc); } } return result.append(name.substring(name.length() - 1, name.length()).toLowerCase()).toString(); }OK,现在测试全部通过了,现在是不是万事大吉了呢?NO,NO,NO,代码写的太糙了,只是为了我们测试通过,现在我们修改自己的测试代码,让代码更加漂亮点。
修改后的测试代码如下:
package test.junit4test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import org.junit.Before; import org.junit.Test; public class LinkinTest { private static final String JAVANAME = "userInfo"; private static final String DBNAME = "user_info"; Linkin linkin = null; @Before public void setUp() { linkin = new Linkin(); } @Test // 测试字符串正常的情况 public void testUnderScoreName4Normal() { String underscoreName = linkin.underscoreName(JAVANAME); assertEquals(DBNAME, underscoreName); } @Test // 测试字符串为null的情况 public void testUnderScoreName4Null() { String underscoreName = linkin.underscoreName(null); assertNull(underscoreName); } @Test // 测试字符串为空字符串的情况 public void testUnderScoreName4Empty() { String underscoreName = linkin.underscoreName(""); assertEquals("", underscoreName); } @Test // 测试当首字母大写时的情况 public void testUnderScoreName4Begin() { String underscoreName = linkin.underscoreName("UserInfo"); assertEquals(DBNAME, underscoreName); } @Test // 测试当尾字母为大写时的情况 public void testUnderScoreName4End() { String underscoreName = linkin.underscoreName("userInfO"); assertEquals(DBNAME, underscoreName); } @Test // 测试多个相连字母大写时的情况 public void testUnderScoreName4Together() { String underscoreName = linkin.underscoreName("userINfo"); assertEquals(DBNAME, underscoreName); } }原来的被测试源码如下:
package test.junit4test; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Linkin { /** * @创建时间: 2016年1月28日 * @相关参数: @param name * @相关参数: @return * @功能描述:格式化一个Java驼峰规则的字符串成数据库规则。 */ public String underscoreName(String name) { if (name == null) { return null; } if ("".equals(name)) { return ""; } StringBuilder result = new StringBuilder().append(name.substring(0, 1).toLowerCase()); for (int i = 1; i < name.length() - 1; i++) { String s = name.substring(i, i + 1); String slc = s.toLowerCase(); String pres = name.substring(i - 1, i); String preslc = pres.toLowerCase(); if (!s.equals(slc) && pres.equals(preslc)) { result.append("_").append(slc); } else { result.append(slc); } } return result.append(name.substring(name.length() - 1, name.length()).toLowerCase()).toString(); } /** * @创建时间: 2016年1月28日 * @相关参数: @param name Java对象名称 * @相关参数: @return 格式化后的名称 * @功能描述: 将Java对象名称(每个单词的头字母大写)按照数据库命名的习惯进行格式化 * <p> * 格式化后的数据为小写字母,并且使用下划线分割命名单词。 * 如果参数name为null,则返回null。 * 例如:employeeInfo 经过格式化之后变为 employee_info * </p> */ public static String wordFormat4DB(String name) { if (name == null) { return null; } Pattern p = Pattern.compile("[A-Z]"); Matcher m = p.matcher(name); StringBuffer sb = new StringBuffer(); while (m.find()) { if (m.start() != 0) { m.appendReplacement(sb, ("_" + m.group()).toLowerCase()); } } return m.appendTail(sb).toString().toLowerCase(); } }上面的这段代码有2个方法,第一个方法我是从spring-jdbc-template中复制出来,但是很遗憾并没有严格的通过我自己写的单元测试。下面的代码我自己写的,也实现了类似的功能。如果感兴趣,可以自行补上测试。
总结:
1,JUnit 将测试失败的情况分为两种:failure 和 error。Failure 一般由单元测试使用的断言方法判断失败引起,它表示在测试点发现了问题;而 error 则是由代码异常引起,这是测试目的之外的发现,它可能产生于测试代码本身的错误(测试代码也
是代码,同样无法保证完全没有缺陷),也可能是被测试代码中的 一个隐藏的bug。
2,再次强调,单元测试只能保证你的代码没有错,但是不能保证你的代码是对的。异常测试是一个很重要的很重要的一部分,一定不能忽略这部分测试。
3,及时的重构源代码,有单元测试在不怕重构没法测试,重构对于自己的源码的结构和设计都会是越来越好,比如什么高内聚,低耦合,比如什么单一职责,比如取消重复等等。
4,及时的维护测试代码,测试代码也是代码,也需要我们维护,这样很方便我们向后兼容迭代代码。比如说上面的测试类,我们写了大量的重复,每次都是传入1个字符串然后去比较我们的预期和实际,在后面的系列中我会讲到一只小怪兽,Parameterized这个后面再说。
junit4初体验就先体验到这里,下一篇我会重点整理下junit4的几个常用的注解和使用。