模拟测试没有返回任何值Java Junit

时间:2017-04-21 05:34:23

标签: java hibernate unit-testing junit mocking

我正在尝试使用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()); } } 返回结果时,它会返回一个空列表。

有人可以帮我解决这个问题吗?

2 个答案:

答案 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;因此,我们使用TransactionUserDAOImpl班级提供这些对象。我们假设方法Mockitosave(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的教程。换句话说:在参加跨栏比赛之前学会爬行!