我正在尝试使用junit和mock来测试使用hibernate连接到db的方法 这是我的代码
UserDAO.java
var expandLink = $('.accordion-expand-all');
expandLink.click(function () {
$(".ui-accordion-content").toggle();
});
UserDAOImpl.java
public interface UserDAO {
public void addUser(String username, String password);
public List<String> getUsers();
}
TestUserDAOImpl
public class UserDAOImpl implements UserDAO {
public static final Logger LOG = LoggerFactory.getLogger(UserDAOImpl.class);
private static Session session;
public UserDAOImpl() {
}
public UserDAOImpl(Session session) {
this.session = session;
}
private static void beginSession() {
session = DbUtils.getSessionFactory().openSession();
session.beginTransaction();
}
@Override
public void addUser(String username, String password) {
String encryptedPassword = Utils.encrypt(password);
User user = new User(username, encryptedPassword);
beginSession();
try {
session.save(user);
System.out.println(user.getPassword());
session.getTransaction().commit();
} catch (SQLGrammarException e) {
session.getTransaction().rollback();
LOG.error("Cannot save user", e);
} finally {
session.close();
}
}
@Override
public List<String> getUsers() {
beginSession();
List<String> results = new ArrayList<String>();
String hql = "select username from User";
Query query = null;
try {
query = session.createQuery(hql);
results = query.list();
} catch (HibernateException e) {
LOG.error("Cannot execute query", e);
}
return results;
}
}
测试用例会向db添加一组用户名和密码,但是当我尝试使用import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class TestUserDAOImpl {
@Mock
SessionFactory sessionFactory;
@Mock
Session session;
@Before
public void setup() {
when(sessionFactory.getCurrentSession()).thenReturn(session);
}
@Test
public void testCreate() {
// userDAOImpl = new UserDAOImpl(session);
UserDAOImpl userDAOImpl = Mockito.mock(UserDAOImpl.class);
String username = "username";
String password = "password";
userDAOImpl.addUser(username, password);
System.out.println(userDAOImpl.getUsers());
}
}
返回结果时,它会返回一个空列表。
有人可以帮我解决这个问题吗?
答案 0 :(得分:2)
首先,您没有向数据库添加任何用户,因为userDAOImpl
是一个模拟对象,因此,正如 Joe C 指出的那样,addUser
方法永远不会被称为真实对象。出于同样的原因(userDAOImpl
被模拟),getUsers
方法不会返回任何列表。
正如您已告诉sessionFactory
该怎么做,然后调用其getCurrentSession()
方法,您可以告诉userDAOImpl
当其方法addUser
和{{1}时该做什么被叫。
作为旁注 :getUsers
方法不应包含testCreate()
方法,因为System.out.println
无法知道您是否测试应该通过或失败。如果您使用的是JUnit
,则可以使用Mockito
方法确保某些代码行正在执行。
或者,如果要测试存储库,可以使用内存数据库并创建实际对象以将数据插入数据库并从中读取数据。这样您的主数据库就不会受到测试数据的污染。这是内存测试数据库中的一个很好的article。
更新:使用Mockito
测试verify
课程
我做的第一件事是,改变了UserDAOImpl
类。原因是:你不能使用UserDAOImpl
来模拟静态方法(至少在写这篇文章的时候不是这样)。有关此问题的更多信息here。
我将Mockito
对象传递给session
并使用该会话开始交易,而不是使用UserDAOImpl
的静态方法。
这是修改后的DbUtils
类:
UserDAOImpl
让我们看看如何使用模拟对象测试package test.mockito;
import java.util.ArrayList;
import java.util.List;
public class UserDAOImpl implements UserDAO {
public static final Logger LOG = LoggerFactory.getLogger(UserDAOImpl.class);
private Session session;
public UserDAOImpl(Session session) {
this.session = session;
}
@Override
public void addUser(String username, String password) {
String encryptedPassword = Utils.encrypt(password);
User user = new User(username, encryptedPassword);
session.beginTransaction();
try {
session.save(user);
System.out.println(user.getPassword());
session.getTransaction().commit();
} catch (SQLGrammarException e) {
session.getTransaction().rollback();
if(LOG != null)LOG.error("Cannot save user", e);
} finally {
session.close();
}
}
@Override
public List<String> getUsers() {
session.beginTransaction();
List<String> results = new ArrayList<String>();
String hql = "select username from User";
Query query = null;
try {
query = session.createQuery(hql);
results = query.list();
} catch (HibernateException e) {
if(LOG != null)LOG.error("Cannot execute query", e);
}
return results;
}
}
的方法。最初,我们模拟我们没有测试的对象,但是为了执行被测代码的语句,我们需要它们。
UserDAOImpl
注意:我们没有测试package test.mockito;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class UserDAOImplTest {
@Mock
Session session;
@Mock
Transaction transaction;
@Before
public void setUp() {
when(session.getTransaction()).thenReturn(transaction);
}
@Test
public void addUserTest() {
UserDAO userDAO = new UserDAOImpl(session);
userDAO.addUser("testusername", "testpassword");
try {
verify(session).getTransaction();
verify(session.getTransaction()).commit();
} catch (SQLGrammarException e) {
fail(e.getMessage());
}
verify(session).close();
}
}
,我们也没有测试Session
;因此,我们使用Transaction
向UserDAOImpl
班级提供这些对象。我们假设方法Mockito
,save(User user)
和其他模拟对象方法正常工作,因此我们只测试commit()
方法。使用addUser
我们不需要建立与数据库的真实会话,而是模拟对象,这样更容易,更快(此外,它可以测试Mockito
方法即使我们还没有开发代码的其他部分 - 其他一些开发人员可能正在研究它。)
在上面的测试用例中,addUser
方法测试当模拟对象的方法按预期运行时此方法是否正确执行。通过使用addUserTest()
,我们确保调用verify(session.getTransacion()).commit()
方法,否则在commit()
块中测试失败。
以同样的方式,我们可以测试catch
在抛出异常时是否回滚事务。以下是如何做到的:
addUser
此测试用例测试@Test
public void addUserTestFails() {
UserDAO userDAO = new UserDAOImpl(session);
try {
doThrow(new SQLGrammarException()).when(session).save(any());
userDAO.addUser("testusername", "testpassword");
verify(transaction, never()).commit();
} catch (SQLGrammarException e) {
verify(transaction).rollback();
}
}
方法,如果在保存更改时抛出异常时表现为例外。通过告诉模拟对象在使用这段代码addUser
调用save
方法时抛出异常来模拟异常,然后确保永远不会调用doThrow(new SQLGrammarException()).when(session).save(any());
方法使用commit()
。在verify(transaction, never()).commit();
块中,您验证事务是否已回退:catch
。
这是包含这两个测试用例的完整类:
verify(transaction).rollback()
答案 1 :(得分:1)
建议:退后一步。现在
使用JUnit和Mockito测试hibernate和DOA是没有意义的......当你缺乏基本的理解如何来编写单元测试时。在您的代码中:
@Test
public void testCreate() {
// userDAOImpl = new UserDAOImpl(session);
UserDAOImpl userDAOImpl = Mockito.mock(UserDAOImpl.class);
String username = "username";
String password = "password";
userDAOImpl.addUser(username, password);
System.out.println(userDAOImpl.getUsers());
}
几乎没有任何意义。典型的单元测试更像是:
class UnderTest {
Foo foo;
UnderTest(Foo foo) { this.foo = foo; }
int bar(String whatever) { return foo.bar(whatever); }
}
class UnderTestTest {
@Mock
Foo foo;
UnderTest underTest;
@Before
public void setup() { underTest = new UnderTest(foo); }
@Test
public void testBar() {
when(foo.bar(any()).thenReturn(5);
assertThat(underTest.bar(), is(5);
}
}
注意:
长话短说:您应该花几天时间阅读有关JUnit和Mockito的教程。换句话说:在参加跨栏比赛之前学会爬行!