2017-04-21 87 views
1

我想測試連接到使用junit和嘲諷的休眠數據庫的方法。
這裏是我的代碼模擬測試不返回任何值Java Junit

UserDAO.java

public interface UserDAO {  
    public void addUser(String username, String password); 
    public List<String> getUsers(); 
} 

UserDAOImpl.java

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; 
    } 
} 

TestUserDAOImpl

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()); 
    } 
} 

測試案例增加了一套用戶名和密碼的數據庫,但是當我嘗試使用getUsers()返回結果它返回一個空列表。

任何人都可以請幫我解決這個問題嗎?

+1

從您的'addUser'方法開始,您的真實對象永遠不會被調用,因爲您使用的是Mockito版本。 –

+0

我該如何解決這個問題?即使在真實對象中調用addUser方法,也不應損壞主數據庫。 – Newbie

回答

2

首先,你不添加任何用戶數據庫,因爲userDAOImpl是一種嘲笑的對象,因此,作爲喬ç指出,該addUser方法不會被調用真正的對象。出於同樣的原因(userDAOImpl被模擬)方法不會返回任何列表。

正如你已經告訴sessionFactory做什麼那麼它getCurrentSession()方法被調用時,你可以告訴做什麼userDAOImpl時,其方法addUser和被調用。

作爲一個方面說明:該testCreate()方法不應該包含System.out.println方法,因爲JUnit無法知道你的測試是否合格或不合格。如果您使用Mockito,則可以使用verify方法確保某些代碼行正在執行。

或者,如果要測試存儲庫,則可以使用內存數據庫並創建實際對象以將數據插入數據庫和從數據庫讀取數據。這樣你的主數據庫不會受到測試數據的污染。在內存測試數據庫中,這是一個很好的article


UPDATE測試使用的Mockito

我做的第一件事是在UserDAOImpl類,改變了UserDAOImpl類一點。原因是:你不能嘲笑使用Mockito的靜態方法(至少在寫這篇文章的時候沒有)。更多關於here

我將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;因此我們使用Mockito將這些對象提供給我們的UserDAOImpl類。我們假設方法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方法,如果它的行爲除外,當有同時節省了變化引發的異常。通過使用這段代碼doThrow(new SQLGrammarException()).when(session).save(any());調用save方法,並通過使用verify(transaction, never()).commit();決不會調用commit()方法,通過告訴模擬對象拋出異常來模擬異常。在catch塊中,您驗證事務回滾:verify(transaction).rollback()

這裏是包含這兩個測試用例完整的類:

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(); 
    } 

    @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(); 
     } 
    } 
} 
+0

如何告訴userDAOImpl addUser和getUser被調用時該怎麼做。 '當userDAOImpl.addUser(username,password).thenReturns(「」)'實際上會引發編譯錯誤 – Newbie

+0

你想測試什麼?如果你想測試一個特定的方法,那麼它不應該被截斷。在真實對象上調用該方法,但是您只模擬了該方法需要的對象,並且在測試期間無法在運行時提供該對象,例如會話,某些數據庫連接等。 – ahoxha

+0

如果需要模擬無效方法,請查看這[鏈接](http://stackoverflow.com/questions/2276271/how-to-make-mock-to-void-methods-with-mockito)。 – ahoxha

1

建議:退一步。現在。

使用JUnit和Mockito測試休眠和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的教程。換句話說:在跨欄跑之前學會爬行!