0

1 つの問題を解決するためにあなたの助けが必要です。DAO クラスに Role を保存するメソッドがあります。

@Repository(value="jdbcRoleDAO")
@Transactional(propagation=Propagation.SUPPORTS, readOnly=true, rollbackFor=Exception.class)
public class JdbcRoleDAO implements RoleDAO {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private DBLogger<Role> dbLogger;

    /**
     * @throws DublicateEntryException if object with specified unique for application field already 
     *              exists in DB.
     */
    @CacheEvict(value = "roles", allEntries = true)
    @Transactional(propagation=Propagation.REQUIRED, readOnly=false, rollbackFor=Exception.class)
    @Override
    public Role save(final Role role) {

        if (this.getRoleByName(role.getName()) != null) {
            throw new DublicateEntryException("Record with specified role name already exists in application.");
        }

        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcTemplate.update(new PreparedStatementCreator() {

            @Override
            public PreparedStatement createPreparedStatement(Connection connection)
                    throws SQLException {

                PreparedStatement ps = connection.prepareStatement(SQL_INSERT_ROLE, new String[]{"ID"});

                ps.setString(1, role.getName());
                ps.setString(2, role.getDesription());
                ps.setObject(3, (role.getParentRole()!=null)?role.getParentRole().getId():null);
                ps.setString(4, role.getPrivilege().toString());

                return ps;
            }
        }, keyHolder);

        Long key = (Long) keyHolder.getKey();
        if (key != null) {
            role.setId(key);
        } else {
            role.setId(-1);
        }

        // Add information about operation to log table
        dbLogger.logBasicOperation(role, OperationName.CREATE);

        return role;
    }
...
}

そして、Mockito を使用して、このメソッドの単体テストを作成したいと考えています。今のところ、次のようになります。

@RunWith(MockitoJUnitRunner.class)
public class JdbcRoleDAOTest {

    private static final long TEST_ROLE_ID = 1L;
    private static final int TEST_ROLE_VERSION = 7;

    @Mock
    private DBLogger<Role> dbLogger;

    @Mock
    private JdbcTemplate jdbcTemplate;

    @InjectMocks
    private JdbcRoleDAO roleDAO;

    /**
     * test role that is used in all methods.
     */
    private Role role;

    @Before
    public void setUp(){
        // Create a test role object
        role = new Role();
        role.setId(TEST_ROLE_ID);
        role.setName("TEST");
        role.setDesription("Test role");
        role.setPrivilege(Privilege.DEFAULT);
        role.setVersion(TEST_ROLE_VERSION);

        // Return correct version of the object
        Mockito.when(jdbcTemplate.queryForInt(JdbcRoleDAO.SQL_GET_VERSION, TEST_ROLE_ID))
            .thenReturn(TEST_ROLE_VERSION);
    }

    @Test
    public void testSave() {
        Assert.assertNotNull(role);

        roleDAO.save(role);

        InOrder inOrder = Mockito.inOrder(jdbcTemplate, dbLogger);

        // Verify execution of the conditions.
        inOrder.verify(jdbcTemplate, Mockito.times(1)).update(Mockito.any(PreparedStatementCreator.class), 
                Mockito.any(KeyHolder.class));

        inOrder.verify(dbLogger, Mockito.times(1)).logBasicOperation(role, OperationName.CREATE);

        /* 
         * Expected -1 because I can't mock KeyHolder and should be returned -1 value (because 
         * KeyHolder will return null instead of key)
         */ 
        Assert.assertEquals("Generated id is wrong!", -1, role.getId());
    }
…
}

問題は、どういうわけか KeyHolder をモックしたいということですが、これを行う方法がわかりません。ご覧のとおり、save メソッドで KeyHolder インスタンスが作成されるようになりました。これが主な問題です。スコープ「プロトタイプ」でSpringを使用してKeyHolderを注入することを考えましたが、新しいロールを同時に保存しようとするユーザーが少ない場合、これが問題を引き起こす可能性があることを理解しています。また、私はこのようなことをしようとしました:

Mockito.when(jdbcTemplate.update(Mockito.any(PreparedStatementCreator.class), 
                Mockito.any(KeyHolder.class)))
                .thenAnswer(new Answer<Void>() {

                    @Override
                    public Void answer(InvocationOnMock invocation)
                            throws Throwable {

                        Object[] arguments = invocation.getArguments();
                        for (int i=0; i<arguments.length; i++) {
                            if (arguments[i] instanceof KeyHolder) {
                                KeyHolder keyHolder = (KeyHolder) arguments[i];

                                KeyHolder spyKeyHolder = Mockito.spy(keyHolder);
                                Mockito.when(spyKeyHolder.getKey()).thenReturn(TEST_GENERATED_ID);
                                Mockito.doReturn(TEST_GENERATED_ID).when(spyKeyHolder).getKey();
                            }
                        }

                        return null;
                    }
                });

しかし、これはうまくいきません。KeyHolder を save メソッドから外に移動せずにモック (またはスパイ) する方法を教えてください。これを可能にするMockitoの機能はありますか? それとも無理ですか?

前もって感謝します

4

2 に答える 2

5

次のインターフェイスを作成できます。

public interface KeyHolderFactory {
     KeyHolder newKeyHolder();
}

KeyHolderFactory次に、内にフィールドを追加してJdbcRoleDAO、本番環境での依存性注入によって入力し、テスト中にモックを手動で入力します。製品の実装は簡単で、単一のインスタンス (シングルトン) を使用できます。

public class GeneratedKeyHolderFactory implements KeyHolderFactory {
     public KeyHolder newKeyHolder() {
         return new GeneratedKeyHolder();
     }
}

あなたが持つメソッドの中に

...
KeyHolder keyHolder = factory.newKeyHolder();
...
于 2012-07-23T15:44:42.583 に答える
0

PowerMockに切り替えることができる場合は、コンストラクターもモックできます。次に、GeneratedKeyHolder コンストラクターをモックして、モック KeyHolder を返すことができます。

于 2012-07-23T15:20:13.627 に答える