Spring JUnit и Mockito - SimpleJdbcTemplate

Учитывая класс, расширяющий SimpleJdbcDaoSupport, как вы можете издеваться над SimpleJdbcTemplate?

public class SimpleJdbcDaoSupportExtension extends SimpleJdbcDaoSupport {  
     public SimpleJdbcDaoSupportExtension (JdbcTemplate jdbcTemplate){  
             super.setJdbcTemplate(jdbcTemplate);  
     }

     public MyDomainObj getResult(){
         SimpleJdbcTemplate sjdbc = getSimpleJdbcTemplate();  
         MyDomainObj result = sjdbc.query(*whatever necessary args*.);
         return result;
     }
}

Затем с помощью Mockito:

public class Test {  
    @Mock private JdbcTemplate mockedJdbcTemplateDedendency;  
    private SimpleJdbcDaoSupportExtension testObj;  

    @Before
    public void doBeforeEachTestCase() {
        MockitoAnnotations.initMocks(this);
        SimpleJdbcDaoSupportExtension sje = new SimpleJdbcDaoSupportExtension (mockedJdbcTemplateDedendency);
    }  
    @Test
    public final void test(){           
        when(mockedJdbcTemplateDedendency.query("what to query").thenReturn(new MyDomainObj());
    }
}

Имитация JdbcTemplate внедряется, но поскольку класс dao полагается на SimpleJdbcTemplate для выполнения запросов (для сопоставления с объектами) и создается внутри SimpleJdbcDaoSupport, фиктивный JdcbTemplate не влияет на SimpleJdbcTemplate. Итак, как это сделать, когда для него нет общедоступных сеттеров, и единственный способ настроить SimpleJdbcTemplate - это полагаться на этот метод, getSimpleJdbcObject ()?


person Dmitri    schedule 11.02.2011    source источник
comment
Не могли бы вы просто ввести его с помощью ReflectionUtils: static.springsource.org/spring/docs/3.0.x/api/org/?   -  person esaj    schedule 12.02.2011


Ответы (4)


Вместо того, чтобы издеваться над конкретным классом, вы должны издеваться над интерфейсом (в котором есть нужные вам методы).

e.g.:

public class SimpleJdbcDaoSupportExtension extends SimpleJdbcDaoSupport implements MyDomainDao{  
     public SimpleJdbcDaoSupportExtension (JdbcTemplate jdbcTemplate){  
             super.setJdbcTemplate(jdbcTemplate);  
     }

     public MyDomainObj getResult(){
         SimpleJdbcTemplate sjdbc = getSimpleJdbcTemplate();  
         MyDomainObj result = sjdbc.query(*whatever necessary args*.);
         return result;
     }
}

public class Test {  
    @Mock private MyDomainDao myDomainDao ;
    private YourController yourController;  

    @Before
    public void doBeforeEachTestCase() {
        MockitoAnnotations.initMocks(this);
        yourController = new YourController(myDomainDao);
    }  
    @Test
    public final void testSomething(){           
        when(myDomainDao.getResult().thenReturn(new MyDomainObj());
        //on to testing the usages of myDomainDao
        yourController.doSomething();
        //verify
        verify(myDomainDao, times(2)).getResult();
    }
}
person Chii    schedule 15.02.2011

Зачем вам издеваться над JdbcTemplate? Используйте настоящую вещь с базой данных в памяти, такой как HSQL.

person Sean Patrick Floyd    schedule 12.02.2011
comment
Я всегда задавался вопросом, если вы используете HSQL вместо насмешек, вы бы каким-то образом не выполняли модульный тест каким-то интеграционным тестом, я прав? - person Juan Antonio Gomez Moriano; 24.04.2013
comment
@JuanAntonioGomezMoriano с точки зрения пуристов, да. Я считаю, что границы между модульными и интеграционными тестами четко не определены. Определение, которое я считаю наиболее полезным (хотя и не семантически правильным): модульный тест - это все, что не требует внешних сервисов, а интеграционный тест - это все остальное. С этой точки зрения БД в памяти подходят для модульных тестов. - person Sean Patrick Floyd; 24.04.2013
comment
Спасибо за объяснение :) - person Juan Antonio Gomez Moriano; 24.04.2013
comment
@SeanPatrickFloyd, а как насчет того, чтобы в моем коде использовалось 50+ таблиц с 200+ столбцами в каждой таблице. Вы создаете всю схему БД в модульных тестах ?? Я не понимаю ... - person Stunner; 18.02.2021

И вот еще. Я решил эту проблему, заменив SimpleJdbcTemplate на JdbcTemplate. Я как бы новичок в Java, перешедший из мира .Net, и изначально выбрал SimpleJdbcTemplate только потому, что наткнулся на некоторую документацию, описывающую его использование, которая полностью отвечала моим потребностям. Но комментарии Скаффмана побудили меня более глубоко изучить JdbcTemplate, а затем я понял, что мне действительно не нужен SimpleJdbcTemplate.
Тем не менее, философская часть вопроса все еще остается в силе . Как высмеивать то, что можно создать, только задав запрос самому контейнеру? Мне кажется, что Spring здесь нарушает принцип DI, делая жесткую зависимость от контейнера.

person Dmitri    schedule 13.02.2011
comment
Точный ответ на ваш вопрос заключается в том, что вам нужно будет использовать имитирующую библиотеку, которая поддерживает имитацию неопределенных классов реализации из заданного базового типа. JMockit (разрабатываемый мной инструмент для имитации) предоставляет указанную поддержку через аннотацию @Capturing, используемую для объявления имитационного поля / параметра. - person Rogério; 08.01.2015

Другой способ, который позволяет вам протестировать больше дао, - это имитировать соединение, подготовленное заявление и набор результатов. Это немного больше, чем просто имитация jdbctemplate, но позволит вам проверить, правильно ли установлено подготовленное заявление и правильно ли работает ваш настраиваемый сопоставитель строк.

public class Test {  
  private MyDao dao;  
  private JdbcTemplate jdbcTemplate;
  private Connection conn;
  private PreparedStatement ps;
  private ResultSet rs;

@Before
public void setUp() throws Exception {
    dao = new MyDao();
    conn = mock(Connection.class);      
    ps = mock(PreparedStatement.class);
    rs = mock(ResultSet.class);
    jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, false));
    doa.setJdbcTemplate(jdbcTemplate);
}

@Test
public void test() throws Exception {           
    when(conn.prepareStatement(any(String.class))).thenReturn(ps);
    when(ps.executeQuery()).thenReturn(rs);
    // return one row
    when(rs.next()).thenReturn(true).thenReturn(false);

    when(rs.getInt("id")).thenReturn(1234);
    when(rs.getString("name")).thenReturn("Bob");

    MyDto myDto = dao.someDaoMethod(...)

    // verify ParameterSource
    verify(ps, times(1)).setInt(1, 1234);

    // these verify if you are mapping the columns to the right object attribute.
    assertEquals(1234, myDto.getId().intValue());
    assertEquals("Bob", myDto.getName());
}
}
person gary    schedule 08.01.2015