以下是对数据库进行单元测试的一个例子.
这是一个web程序,servlet从request里提取SQL语句,然后从DB里取得数据返回给JSP.
首先,假设你的数据库访问代码与业务逻辑代码已经清晰地区分开来.那么我们就可以对数据库访问进行不同的单元测试.
1.对业务逻辑的单元测试--------这些测试的目标是将业务逻辑代码部分与数据库访问代码部分(持久化代码JDBC/JDO...)隔离开来,只对业务逻辑代码部分进行单元测试.采用什么策略?当然是mock objects.:-) 以下是例子:
public class AdminServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException {}
public String getCommand(HttpServletRequest request) throws ServletException {} //从request里取得SQL语句
public Collection executeCommand(String command) throws Exception {}
public void callView(HttpServletRequest request) {} //传递数据给JSP
}
各种不同的方法的执行流程是:doGet接受HTTP请求并调用getCommand方法抽取出其中的SQL查询,接着调用executeCommand 来执行数据库调用并将结果以一个Collection返回.结果放入HTTP请求中,最后,doGet调用callView来激活给用户显示结果的JSP 页面.
现在的问题在于如何在不执行由executeCommand运行的数据库访问代码的前提下对doGet, getCommand,callView进行单元测试.方法是建立数据库访问层.为数据库访问层建立一个接口.有了接口就可以采用 mock objects策略来对数据库访问层进行单元测试.
数据库访问层接口:
public interface DataAccessManager {
Collection execute(String sql) throws Exception;
}
接着重构AdminServlet类
public class AdminServlet extends HttpServlet
{
// [...]
private DataAccessManager dataManager;
public void init() throws ServletException
{
super.init();
try
{
this.dataManager = new JdbcDataAccessManager();
catch (NamingException e)
{
throw new ServletException(e);
}
}
public Collection executeCommand(String command)
throws Exception
{
return this.dataManager.execute(command);
}
}
JdbcDataAccessManager 类是DataAccessManager的执行类. 在进行单元测试时我们所要做的是建立一个DataAccessManager的 mock objects实现来替换真实的JdbcDataAccessManager.
继续重构成AdminServlet:
public class AdminServlet extends HttpServlet
{
// [...]
private DataAccessManager dataManager;
public DataAccessManager getDataAccessManager()
{
return this.dataManager;
}
public void setDataAccessManager(DataAccessManager manager)
{
this.dataManager = manager;
}
public void init() throws ServletException
{
super.init();
try
{
setDataAccessManager(new JdbcDataAccessManager());
}
catch (NamingException e)
{
throw new ServletException(e);
}
}
public Collection executeCommand(String command)
throws Exception
{
return this.dataManager.execute(command);
}
}
添加一个setter DataAccessManager的方法来便于传入mock objects.
下面就是怎样来建立mock objects,我们用DynaMock API来实现,当然你也可以用easymock,JMock等来实现.下面是一个编写DynaMock TestCase的模板:
import java.util.ArrayList;
import com.mockobjects.dynamic.Mock;
import com.mockobjects.dynamic.C;
import junit.framework.TestCase;
public class TestAdminServletDynaMock extends TestCase
{
public void testSomething() throws Exception
{
Mock mockManager = new Mock(DataAccessManager.class);
DataAccessManager manager =
(DataAccessManager) mockManager.proxy();
mockManager.expectAndReturn("execute", C.ANY_ARGS,
new ArrayList());
AdminServlet servlet = new AdminServlet();
servlet.setDataAccessManager(manager);
// Call the method to test here. For example:
// manager.doGet(request, response)
// [...]
}
}
你用DynaMockAPI创建一个DataAccessManager mock objects,接下来当调用execute方法时你让 mock objects返回你设定的值.那么AdminServlet其余的方法就可以很方便的用这个值进行单元测试了.
这是一个web程序,servlet从request里提取SQL语句,然后从DB里取得数据返回给JSP.
首先,假设你的数据库访问代码与业务逻辑代码已经清晰地区分开来.那么我们就可以对数据库访问进行不同的单元测试.
1.对业务逻辑的单元测试--------这些测试的目标是将业务逻辑代码部分与数据库访问代码部分(持久化代码JDBC/JDO...)隔离开来,只对业务逻辑代码部分进行单元测试.采用什么策略?当然是mock objects.:-) 以下是例子:
public class AdminServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException {}
public String getCommand(HttpServletRequest request) throws ServletException {} //从request里取得SQL语句
public Collection executeCommand(String command) throws Exception {}
public void callView(HttpServletRequest request) {} //传递数据给JSP
}
各种不同的方法的执行流程是:doGet接受HTTP请求并调用getCommand方法抽取出其中的SQL查询,接着调用executeCommand 来执行数据库调用并将结果以一个Collection返回.结果放入HTTP请求中,最后,doGet调用callView来激活给用户显示结果的JSP 页面.
现在的问题在于如何在不执行由executeCommand运行的数据库访问代码的前提下对doGet, getCommand,callView进行单元测试.方法是建立数据库访问层.为数据库访问层建立一个接口.有了接口就可以采用 mock objects策略来对数据库访问层进行单元测试.
数据库访问层接口:
public interface DataAccessManager {
Collection execute(String sql) throws Exception;
}
接着重构AdminServlet类
public class AdminServlet extends HttpServlet
{
// [...]
private DataAccessManager dataManager;
public void init() throws ServletException
{
super.init();
try
{
this.dataManager = new JdbcDataAccessManager();
catch (NamingException e)
{
throw new ServletException(e);
}
}
public Collection executeCommand(String command)
throws Exception
{
return this.dataManager.execute(command);
}
}
JdbcDataAccessManager 类是DataAccessManager的执行类. 在进行单元测试时我们所要做的是建立一个DataAccessManager的 mock objects实现来替换真实的JdbcDataAccessManager.
继续重构成AdminServlet:
public class AdminServlet extends HttpServlet
{
// [...]
private DataAccessManager dataManager;
public DataAccessManager getDataAccessManager()
{
return this.dataManager;
}
public void setDataAccessManager(DataAccessManager manager)
{
this.dataManager = manager;
}
public void init() throws ServletException
{
super.init();
try
{
setDataAccessManager(new JdbcDataAccessManager());
}
catch (NamingException e)
{
throw new ServletException(e);
}
}
public Collection executeCommand(String command)
throws Exception
{
return this.dataManager.execute(command);
}
}
添加一个setter DataAccessManager的方法来便于传入mock objects.
下面就是怎样来建立mock objects,我们用DynaMock API来实现,当然你也可以用easymock,JMock等来实现.下面是一个编写DynaMock TestCase的模板:
import java.util.ArrayList;
import com.mockobjects.dynamic.Mock;
import com.mockobjects.dynamic.C;
import junit.framework.TestCase;
public class TestAdminServletDynaMock extends TestCase
{
public void testSomething() throws Exception
{
Mock mockManager = new Mock(DataAccessManager.class);
DataAccessManager manager =
(DataAccessManager) mockManager.proxy();
mockManager.expectAndReturn("execute", C.ANY_ARGS,
new ArrayList());
AdminServlet servlet = new AdminServlet();
servlet.setDataAccessManager(manager);
// Call the method to test here. For example:
// manager.doGet(request, response)
// [...]
}
}
你用DynaMockAPI创建一个DataAccessManager mock objects,接下来当调用execute方法时你让 mock objects返回你设定的值.那么AdminServlet其余的方法就可以很方便的用这个值进行单元测试了.