Pregunta

I am trying to write a unit test to a function that holds the following code:

    KeyHolder newCode = new GeneratedKeyHolder();
    try {
        namedParameterJdbcTemplate.update(sql, paramMap, newCode);
    } catch (DuplicateKeyException e) {
        logger.error("Duplicate Key");
    }
    data.setId(newCode.getKey().intValue());

right now, while using Mockito:

Mockito.when(namedParameterJdbcTemplate.update(Mockito.anyString(), Mockito.any(MapSqlParameterSource.class), Mockito.any(GeneratedKeyHolder.class))).thenReturn(1);

So how am i supposed to fill the GeneratedKeyHolder with Data?

Thanks.

¿Fue útil?

Solución

I would do this in one of the following ways:

  1. Inject the key.
  2. Stub the method call and set the key there.

Method 1 When I say inject the key I actually mean inject a KeyFactory. This means you can control the result within your test. For example:

KeyHolder newCode = injectedKeyFactory.getKeyHolder();
try {
    namedParameterJdbcTemplate.update(sql, paramMap, newCode);
} catch (DuplicateKeyException e) {
    logger.error("Duplicate Key");
}
data.setId(newCode.getKey().intValue());

Then in the test:

KeyHolder newCode = mock(KeyHolder.class);
Mockito.when(namedParameterJdbcTemplate.update(Mockito.anyString(), Mockito.any(MapSqlParameterSource.class), newCode)).thenReturn(1);
Mockito.when(newCode.getKey()).thenReturn(__preferredId__);

The key factory is a simple one which just returns a new GeneratedKeyHolder. It gets injected at construction time, so this method does assume you're using DI.

Method 2

Mockito.when(namedParameterJdbcTemplate.update(Mockito.anyString(), Mockito.any(MapSqlParameterSource.class), Mockito.any(GeneratedKeyHolder.class))).thenAnswer(new Answer() {
    Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        Map<String, Object> keyMap = new HashMap<String, Object>();
        keyMap.put("", __preferredId__);
        ((GeneratedKeyHolder)args[2]).getKeyList().add(keyMap);
    }
}).andReturn(1);

I've not really used Mockito, so sorry if the code's not quite right. :)

Otros consejos

This worked for me. It's slightly different to the answer above.

Class being tested
Declare KeyHolder factory as class property and inject in the constructor:

private final NamedParameterJdbcTemplate jdbcTemplate;
private final GeneratedKeyHolderFactory keyHolderFactory;

public AccountJdbcAdapter(NamedParameterJdbcTemplate jdbcTemplate, GeneratedKeyHolderFactory keyHolderFactory) {
     this.keyHolderFactory = keyHolderFactory;
        this.jdbcTemplate = jdbcTemplate;
}

    private Long executeCreateAccount(Account newAccount){
        KeyHolder keyHolder = keyHolderFactory.newKeyHolder();
        jdbcTemplate.update(
            queryCreateAccount,
            createAccountParams(newAccount),
            keyHolder
        );

      YOU WILL INJECT MOCKED VALUE HERE --> 
      Long accountId = keyHolder.getKey().longValue();

      }

Declare class for the factory

   @Component
    public class GeneratedKeyHolderFactory {
        public GeneratedKeyHolderFactory(){

        }
        public KeyHolder newKeyHolder() {
            return new GeneratedKeyHolder();
        }
    }

Test: Mock the factory, get a new KeyHolder instance and fullfill with the values

private final GeneratedKeyHolderFactory factory = mock(GeneratedKeyHolderFactory.class);
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate = mock(NamedParameterJdbcTemplate.class);
private final AccountJdbcAdapter accountJdbcAdapter = new AccountJdbcAdapter(namedParameterJdbcTemplate, factory);


  void createAccountsOk() {
        KeyHolder keyHolder = new GeneratedKeyHolder(Arrays.asList(Map.of("accountId", accountId)));
        when(factory.newKeyHolder()).thenReturn(keyHolder);

        when(namedParameterJdbcTemplate.update(any(), any(MapSqlParameterSource.class), any(GeneratedKeyHolder.class))).thenReturn(1);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top