我是Mockito和Junit的新手。 为现有类添加测试时遇到问题。
我正在上课,有以下两种方法。 私有方法和公共方法 公共方法调用私有方法,并且在功能方面与私有方法具有相似的结构(即从数据库获取一些东西)
现在,在通过 mockito 测试它时,我正在尝试这样做
Class MyClass {
private String privateMethod(String number){
String sql = null;
java.sql.Connection conn = null;
PreparedStatement pst = null;
ResultSet rs1 = null;
String returnString = "";
try {
conn = dbConnect();
sql = " Select SomeCOL from SOME_TABLE";
pst = conn.prepareStatement(sql);
pst.setString(1, caseNumber);
rs1 = pst.executeQuery();
if(rs1.next())
returnString = rs1.getString(1);
}catch (SQLException e) {
} finally {
//close conn
}
return returnString ;
}
public void publicmethod(String number){
String retVal = privateMethod(number);
if(!StringUtils.isEmpty(retVal)){
String sql = null;
java.sql.Connection conn1 = null;
PreparedStatement pst = null;
ResultSet rs1 = null;
try {
conn1 = dbConnect();
sql = " SOME_SQL_STMNT";
pst = conn1.prepareStatement(sql);
rs1 = pst.executeQuery();
while(rs1.next()){
//do something
}
}catch (SQLException e) {
} finally {
//close conn
}
}
}
}
以下是测试类: 添加了 dono 以跳过对 dbdisconnect 方法的调用,该方法将 null 分配给 conn(将模拟连接转换为 null)
Class MyclassTest {
@InjectMocks
private MyClass mc;
@Mock
java.sql.Connection mockConn;
@Mock private PreparedStatement mockStatement;
@Mock
ResultSet rs;
@Before
public void setup(){
MockitoAnnotations.initMocks(this);
}
try {
when(mockConn.prepareStatement(" Select SomeCOL from SOME_TABLE")).thenReturn(mockStatement);
when(mockStatement.executeQuery()).thenReturn(rs));
when(rs.next()).thenReturn(true).thenReturn(false);
when(rs.getString(1)).thenReturn("123");
} catch (SQLException e) {
}
现在,当我运行这些测试时,我可以看到在privatemethod上模拟了连接,我可以看到返回的值为"123"(尝试抛出手动异常并将消息作为返回值)。但是在公共方法中,我得到了
行处的空指针 pst = conn1.prepareStatement(sql); 因为 conn1 为空。
有没有问题。 我们能处理这个吗?
更新了具有数据库连接方法的 MyClass 超类的代码
public java.sql.Connection dbConnect (String dbname) {
DSname = "SOME_DSLOOKUPNAME";
InitialContext ctx = null;
if (connection == null) {
try {
Hashtable<String, String> ht = new Hashtable<>();
ht.put(Context.PROVIDER_URL, "PROVIDER_PORT");
ctx = new InitialContext(ht);
javax.sql.DataSource ds = (javax.sql.DataSource) ctx.lookup(DSname);
connection = ds.getConnection();
} catch (Exception e) {
}
}
return (connection);
}
最终工作测试实施
Class MyclassTest {
@InjectMocks
private MyClass mc;
@Mock
java.sql.Connection mockConn;
@Mock private PreparedStatement mockStatement;
@Mock
ResultSet rs;
@Spy
MyClass mc1;
@Before
public void setup(){
MockitoAnnotations.initMocks(this);
}
@Test
public void publicMethodTest(){
try {
Mockito.doReturn(mockConn).when(mc1).dbConnect(Mockito.anyString());
when(mockConn.prepareStatement(Mockito.anyString())).thenReturn(mockStatement).thenReturn(mockStatement);
doNothing().when(mc1).dbDisconnect();
when(mockStatement.executeQuery()).thenReturn(rs).thenReturn(rs);
when(rs.next()).thenReturn(true).thenReturn(true).thenReturn(false);
when(rs.getString(1)).thenReturn("123");
} catch (SQLException e) {
}
mc1.publicMethod("12345678");
}
我认为您需要一种方法通过某种构造函数或setter方法在MyClass中注入连接对象。 所以你可以模拟事情。
public class MyClass {
private Connection connection;
public MyClass(Connection connection) {
this.connection = connection;
}
....
....
}
在你的单元测试中,你可以模拟这样的东西:
@Test
public void testConnection() {
Connection mockConnection = mock(Connection.class);
when(mockConnection.prepareStatment("select statements")).thenReturn(mockStatement);
MyClass myClass = new MyClass(mockConnection);
myClass.publicMethod();
}
prepareStatement
方法在privateMethod()
和publicMethod()
中使用不同的参数值调用。因此,您应该:
清楚地模拟每个呼叫:
when(mockConn.prepareStatement(" Select SomeCOL from SOME_TABLE")).thenReturn(mockStatement);
when(mockConn.prepareStatement(" SOME_SQL_STMNT")).thenReturn(mockStatement);
或者有一个更一般的模拟:
when(mockConn.prepareStatement(Mockito.anyString())).thenReturn(mockStatement);