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的几个常用的注解和使用。