2015-02-07 5 views
19

Я начал обнаруживать библиотеку Мокито, и есть вопрос, для которого я не нашел правильного ответа.Как проверить методы DAO с помощью Mockito?

Если у меня есть, например, такой метод в моем классе UserDAO, который сохраняет в базе данных пользователя:

public class UserDAO{ 
... 
public void create(User user) { 
     Connection connection = null; 
     PreparedStatement pstmt = null; 
     ResultSet generatedKeys = null; 
     try { 

      connection = getConnection(); 
      pstmt = connection.prepareStatement(INSERT_USER, 
        PreparedStatement.RETURN_GENERATED_KEYS); 
      int counter = 1; 
      pstmt.setString(counter++, user.getFirstName()); 
      pstmt.setString(counter++, user.getLastName()); 
      pstmt.setString(counter++, user.getEmail()); 
      pstmt.setString(counter++, user.getPassword()); 
      pstmt.setString(counter++, user.getRole()); 
      pstmt.setString(counter, user.getLang()); 

      pstmt.execute(); 
      connection.commit(); 
      generatedKeys = pstmt.getGeneratedKeys(); 

      if (generatedKeys.next()) { 
       user.setId(generatedKeys.getInt(Fields.GENERATED_KEY)); 
      } 
     } catch (SQLException e) { 
      rollback(connection); 
      LOG.error("Can not create a user", e); 
     } finally { 
      close(connection); 
      close(pstmt); 
      close(generatedKeys); 
     } 
    } 
    .... 
} 

Как я должен это проверить?

Если я хочу протестировать, например, класс DAO, мне нужно создать макет DataSource, Connection mock, mock и т. Д.? И чтобы не тестировать базу данных?

Но что, если я хочу также проверить поведение dao и базы данных?

Не могли бы вы предоставить образцы кода, ссылки, которые могут быть полезны и показать лучшие подходы к его выполнению?

+1

Правильный DAO не должен управлять транзакциями, то есть не совершать или откатываться. Обычно демаркация транзакций выполняется на более высоком уровне, и обычно (Java EE, Spring и т. Д.) Применяется неявно. –

ответ

17

Вот неплохо начать использовать Mockito для тестирования вашего UserDAO. Этот код использует хорошее количество функций Mockito, поэтому вы можете увидеть, как их использовать. Дайте мне знать, если у вас есть вопросы.

import java.sql.Connection; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 
import javax.sql.DataSource; 
import org.junit.After; 
import org.junit.AfterClass; 
import org.junit.Before; 
import org.junit.BeforeClass; 
import org.junit.Test; 
import static org.junit.Assert.*; 
import org.junit.runner.RunWith; 
import static org.mockito.Matchers.anyInt; 
import static org.mockito.Matchers.anyString; 
import org.mockito.Mock; 
import static org.mockito.Mockito.doNothing; 
import static org.mockito.Mockito.times; 
import static org.mockito.Mockito.verify; 
import static org.mockito.Mockito.when; 
import org.mockito.runners.MockitoJUnitRunner; 

@RunWith(MockitoJUnitRunner.class) 
public class TestUserDAO { 

    @Mock 
    DataSource mockDataSource; 
    @Mock 
    Connection mockConn; 
    @Mock 
    PreparedStatement mockPreparedStmnt; 
    @Mock 
    ResultSet mockResultSet; 
    int userId = 100; 

    public TestUserDAO() { 
    } 

    @BeforeClass 
    public static void setUpClass() throws Exception { 
    } 

    @AfterClass 
    public static void tearDownClass() { 
    } 

    @Before 
    public void setUp() throws SQLException { 
     when(mockDataSource.getConnection()).thenReturn(mockConn); 
     when(mockDataSource.getConnection(anyString(), anyString())).thenReturn(mockConn); 
     doNothing().when(mockConn).commit(); 
     when(mockConn.prepareStatement(anyString(), anyInt())).thenReturn(mockPreparedStmnt); 
     doNothing().when(mockPreparedStmnt).setString(anyInt(), anyString()); 
     when(mockPreparedStmnt.execute()).thenReturn(Boolean.TRUE); 
     when(mockPreparedStmnt.getGeneratedKeys()).thenReturn(mockResultSet); 
     when(mockResultSet.next()).thenReturn(Boolean.TRUE, Boolean.FALSE); 
     when(mockResultSet.getInt(Fields.GENERATED_KEYS)).thenReturn(userId); 
    } 

    @After 
    public void tearDown() { 
    } 

    @Test 
    public void testCreateWithNoExceptions() throws SQLException { 

     UserDAO instance = new UserDAO(mockDataSource); 
     instance.create(new User()); 

     //verify and assert 
     verify(mockConn, times(1)).prepareStatement(anyString(), anyInt()); 
     verify(mockPreparedStmnt, times(6)).setString(anyInt(), anyString()); 
     verify(mockPreparedStmnt, times(1)).execute(); 
     verify(mockConn, times(1)).commit(); 
     verify(mockResultSet, times(2)).next(); 
     verify(mockResultSet, times(1)).getInt(Fields.GENERATED_KEYS); 
    } 

    @Test(expected = SQLException.class) 
    public void testCreateWithPreparedStmntException() throws SQLException { 

     //mock 
     when(mockConn.prepareStatement(anyString(), anyInt())).thenThrow(new SQLException()); 


     try { 
      UserDAO instance = new UserDAO(mockDataSource); 
      instance.create(new User()); 
     } catch (SQLException se) { 
      //verify and assert 
      verify(mockConn, times(1)).prepareStatement(anyString(), anyInt()); 
      verify(mockPreparedStmnt, times(0)).setString(anyInt(), anyString()); 
      verify(mockPreparedStmnt, times(0)).execute(); 
      verify(mockConn, times(0)).commit(); 
      verify(mockResultSet, times(0)).next(); 
      verify(mockResultSet, times(0)).getInt(Fields.GENERATED_KEYS); 
      throw se; 
     } 

    } 
} 
+0

Я пробовал этот код - он компилируется, но во время выполнения исключение InvalidUseOfMatchersException выбрано из обоих методов. – marknorkin

+0

Вы должны показать, какая строка бросает это. Когда матчи r используются в качестве входных данных для метода, то все входные данные должны быть состыками. –

+0

Выбрасывается из строки: 'когда (mockConn.prepareStatement (anyString(), \t \t PreparedStatement.RETURN_GENERATED_KEYS)). thenReturn (mockPreparedStmnt); ' – marknorkin

6

Но что, если я хочу также проверить поведение dao и базы данных?

Если вы действительно хотите протестировать базу данных (как и следовало бы!), Нет никакого способа обойти ее - вам нужна фактическая база данных. Mockito, хотя и является большой библиотекой, вероятно, является неправильным инструментом для этой работы.

+0

ОК, это очистило что-то. но о случае, когда я хочу проверить только саму dao? – marknorkin

+4

Это интеграционное тестирование. Не то же самое, что и модульное тестирование. –

1

Инструмент вроде DBUnit в сочетании с JUnit может помочь вам протестировать ваши DAO с помощью базы данных. DBUnit помогает вам вставлять тестовые данные в базу данных до вашего UnitTest и сравнивать данные в базе данных с вашими ожиданиями после теста.

1

Вот как вы должны проверить:

public class UserDAOTest extends IntegrationTests 
{ 
    // Or do it in a @Before method, if needed. 
    UserDAO dao = new UserDAO(); 

    @Test 
    public void createValidUser() { 
     User validUser = new User(
      "John", "Smith", "[email protected]", "[email protected]", 
      "admin", "en"); // or use setters as needed 

     dao.create(validUser); 

     assertEntityCreatedInDB(validUser); 
    } 

    @Test 
    public void attemptToCreateInvalidUser() { 
     User invalidUser = new User("", null, null, "", null, "XY"); 

     dao.create(invalidUser); 

     // This really shouldn't be done this way, as DAOs are not supposed 
     // to manage transactions; instead, a suitable, descriptive 
     // exception should be thrown by the DAO and checked in the test. 
     assertTransactionWasRolledBack(); 
    } 
} 

Пара замечаний по поводу выше:

1) Тесты выглядят короткие, простые и легко понять, так как они должны быть; если они выглядят большими и уродливыми, как в другом ответе, вы делаете что-то принципиально неправильное.

2) Тестовый код может и должен иметь свои собственные помощники инфраструктуры, такие как базовый класс IntegrationTests, который скроет любой неприятный доступ JDBC/ORM от фактических тестов. Я реализовал таких помощников в нескольких проектах, поэтому я знаю, что это можно сделать, но это будет полезно для других вопросов.

Смежные вопросы