Jalor3的所有业务代码,基本上都是写在Business类里,再由EJB的Facade类提供事务支持。在不Mock数据库操作的情况下,如何要对Business进行单元测试,是非常困难的。在几经试验与研究之后,我们得到了一个相对比较优雅的解决方案。之所以说它是比较优雅的原因是:我们在写一个Business的时候,除了继承一个用于作单元测试的基类外,基本不用再做其它的事情,即可对待测试的Business方法进行任何透明的调用,无需进行任何特殊处理,就像写正常的业务逻辑一样。下面是对应的基类代码:
-
Java 代码001 | import static org.powermock.api.mockito.PowerMockito.mock; |
002 | import static org.powermock.api.mockito.PowerMockito.mockStatic; |
003 | import static org.powermock.api.mockito.PowerMockito.when; |
005 | import java.io.PrintWriter; |
006 | import java.sql.Connection; |
007 | import java.sql.DriverManager; |
008 | import java.sql.SQLException; |
009 | import java.sql.SQLFeatureNotSupportedException; |
010 | import java.util.Properties; |
012 | import javax.sql.DataSource; |
014 | import org.apache.log4j.Logger; |
015 | import org.junit.Before; |
016 | import org.junit.runner.RunWith; |
017 | import org.powermock.core.classloader.annotations.PrepareForTest; |
018 | import org.powermock.modules.junit4.PowerMockRunner; |
020 | import com.huawei.it.BaseDbTestCase; |
021 | import com.huawei.it.common.Jdbcs; |
022 | import com.huawei.it.core.frame.ServiceLocator; |
023 | import com.huawei.ormapping.sqlmap.client.SqlMapClientBuilder; |
025 | @RunWith (PowerMockRunner. class ) |
026 | @PrepareForTest ({ServiceLocator. class , SqlMapClientBuilder. class }) |
027 | public abstract class BaseBusinessTestCase extends BaseDbTestCase{ |
028 | private static String url; |
029 | private static String usr; |
030 | private static String pwd; |
035 | Properties dbs = new Properties(); |
036 | dbs.load(BaseDbTestCase. class .getResourceAsStream( "/com/huawei/it/db.properties" )); |
038 | url = dbs.getProperty( "url" ); |
039 | usr = dbs.getProperty( "usr" ); |
040 | pwd = dbs.getProperty( "pwd" ); |
043 | Class.forName( "oracle.jdbc.driver.OracleDriver" ); |
044 | Jdbcs.setDataSource(getDataSource()); |
045 | } catch (Exception e) { |
046 | throw new RuntimeException(e); |
050 | protected static synch ronized DataSource getDataSource() { |
051 | return new DataSource() { |
054 | public <T> T unwrap(Class<T> arg0) throws SQLException { |
059 | public boolean isWrapperFor(Class<?> arg0) throws SQLException { |
064 | public void setLoginTimeout( int arg0) throws SQLException { |
068 | public void setLogWriter(PrintWriter arg0) throws SQLException { |
071 | public Logger getParentLogger() throws SQLFeatureNotSupportedException { |
076 | public int getLoginTimeout() throws SQLException { |
081 | public PrintWriter getLogWriter() throws SQLException { |
082 | return new PrintWriter(System.out); |
086 | public Connection getConnection(String username, String password) |
087 | throws SQLException { |
088 | return DriverManager.getConnection(url, username, password); |
092 | public Connection getConnection() throws SQLException { |
093 | return DriverManager.getConnection(url, usr, pwd); |
099 | * 准备Mock对象,置换框架组件相关方法 |
103 | public void setUp() throws Exception{ |
104 | ServiceLocator serviceLocator = mock(ServiceLocator. class ); |
105 | mockStatic(ServiceLocator. class ); |
106 | when(ServiceLocator.getInstance()).thenReturn(serviceLocator); |
107 | when(serviceLocator.getDataSource( "jdbc/jalor3DS" )).thenReturn(getDataSource()); |
这里需要注意的是代码中有两个地方,一个是数据库连接串、用户名、密码的配置文件路径/com/huawei/it/db.properties,另一个是系统所连数据源的JNDI名称,如jdbc/jalor3DS,在实际使用的时候,要替换成自己的实际值。
下面是一个具体Business的测试类,除了通过JDBC做测试前的准备(清理测试环境)和测试后测试数据的删除外,其它都是直接对Business方法进行的直接调用,对调用后的结果进行断言检查。
-
Java 代码01 | import static org.junit.Assert.assertEquals; |
02 | import static org.junit.Assert.assertTrue; |
06 | import org.junit.After; |
07 | import org.junit.Before; |
10 | import com.huawei.it.common.DateUtil; |
11 | import com.huawei.it.common.Jdbcs; |
12 | import com.huawei.it.tcsm.busi.BaseBusinessTestCase; |
13 | import com.huawei.it.tcsm.busi.TcsmCommonBusiness; |
14 | import com.huawei.it.tcsm.vo.MailVO; |
15 | import com.huawei.it.tcsm.vo.TcsmLogVO; |
17 | public class GetLoginLog extends BaseBusinessTestCase { |
18 | private TcsmCommonBusiness busi = new TcsmCommonBusiness(); |
19 | private TcsmLogVO tcsmLogVO = new TcsmLogVO(); |
22 | public void init() throws Exception { |
23 | String usrId = "441047706911511" ; |
24 | Jdbcs.executeUpdate( "delete TPL_HUAWEI_LOG_EVENTS_T where USERID=?" , usrId); |
26 | tcsmLogVO.setUserid(usrId); |
27 | tcsmLogVO.setClass0( "TcsmCommon" ); |
28 | tcsmLogVO.setHostip( "10.37.55.14" ); |
29 | tcsmLogVO.setHostname( "zhuodefang.huawei.com" ); |
30 | tcsmLogVO.setOperate_object( "TcsmCommon" ); |
31 | tcsmLogVO.setOperate( "Login" ); |
32 | tcsmLogVO.setOperate_object_id(String.valueOf(System.currentTimeMillis())); |
33 | tcsmLogVO.setModule( "tcsm_common" ); |
34 | tcsmLogVO.setMessage( "insert a new record" ); |
35 | busi.tcsmLog(tcsmLogVO); |
39 | public void getLoginLog() throws Exception{ |
40 | MailVO m = busi.getLoginLog(tcsmLogVO.getUserid()); |
41 | assertEquals(tcsmLogVO.getHostip(), m.getMailAddress()); |
42 | String dateTime = DateUtil.format( new Date(), "yyyy-MM-dd" ); |
43 | assertTrue(m.getLastUpdateDate().contains(dateTime)); |
47 | public void tearDown() throws Exception { |
48 | Jdbcs.executeUpdate( "delete TPL_HUAWEI_LOG_EVENTS_T where USERID=?" , tcsmLogVO.getUserid()); |
基本上只要继承BaseBusinessTestCase这个测试基类(修改里面的数据源JNDI名和数据库相关配置),后面的一切都非常自然,没有任何的侵入性。至于具体的原理,主要是用到了PowerMock这个Mock框架,对系统中的部分静态方法调用进行了Stub,如ServiceLocator.getDataSource(),ServiceLocator.getInstance,关于PowerMock详细的资料,可以上网搜索,非常多,不在这里讨论。如果有兴趣,欢迎回帖探讨。
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
评论(0)