PreparedStatement.execute () зависает в модульном тесте Spring - PullRequest
1 голос
/ 04 октября 2011

У меня проблемы с юнит-тестами в приложении Spring MVC.В полном раскрытии, есть хороший шанс, что я проектирую свои модульные тесты неправильно, учитывая мой недостаток опыта в написании набора тестов с нуля.

Способ, которым я в настоящее время проектировал это, например, для тестированияпользовательский сервис, набор тестов использует необработанные операторы SQL для проверки правильности вставки / извлечения / обновления данных.Проблема, с которой я столкнулся, заключается в том, что после выполнения подготовленного первого оператора последующие операторы зависают в методе execute().В результате теста получается «Превышено время ожидания блокировки; попробуйте перезапустить транзакцию»

Исходя из того, что я прочитал в Интернете, это, вероятно, проблема управления транзакциями, и кто-то не 'Я снимаю блокировку, но я не уверен, как лучше это сделать или даже где это сделать.

Ниже приведен соответствующий код, дайте мне знать, если потребуется больше кода.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext-base.xml", "/application-security.xml"})
@TransactionConfiguration(transactionManager="txManager")
@Transactional
public class TestUserService {

    @Autowired
    UsersService userService;

    @Autowired
    DataSource dataSource;

    Connection connection;

    @Before
    public void setup() throws Exception{
        connection = dataSource.getConnection();
    }

    @Test
    public void testCreateUser() throws Exception{

        Collection<GrantedAuthorityImpl> auths = new ArrayList<GrantedAuthorityImpl>();
        auths.add(new GrantedAuthorityImpl(SecurityConstants.ROLE_USER));


        User user = new User("testUser", "testpassword", true, true, true, true, auths, "salt");

        User tmp = userService.createUser(user);


        PreparedStatement ps = connection.prepareStatement("select id, username, password, created, enabled, salt from users where id = ?");
        PreparedStatement ps2 = connection.prepareStatement("select user, authority from user_authorities where user = ?");

        ps.setLong(1, tmp.getId());
        ps2.setLong(1, tmp.getId());

        ResultSet rs = ps.executeQuery();
        ResultSet rs2 = ps2.executeQuery();

        rs.first();
        rs2.first();

        Collection<GrantedAuthorityImpl> authsFromDb = new ArrayList<GrantedAuthorityImpl>();

        rs.first();
        do{
            authsFromDb.add(new GrantedAuthorityImpl(rs2.getString("authority")));
        }while(rs2.next());

        User tmp2 = new User(rs.getString("username"), rs.getString("password"), rs.getBoolean("enabled"), true, true, true, authsFromDb, rs.getString("salt"));

        Assert.assertEquals(tmp.getUsername(), tmp2.getUsername());
        Assert.assertEquals(tmp.getId(), tmp2.getId());
        Assert.assertEquals(tmp.getPassword(), tmp2.getPassword());
        Assert.assertEquals(tmp.getSalt(), tmp2.getSalt());
        Assert.assertEquals(tmp.getAuthorities(), tmp2.getAuthorities());
        Assert.assertEquals(tmp.isEnabled(), tmp2.isEnabled());

    }

    @Test
    public void testSaveUser() throws Exception{
        long createdTime = System.currentTimeMillis();
        String insertionQry = "insert into users (username, password, created, enabled, salt) values ('chris', 'somepassword'," + createdTime + ",1,'salt')";


        PreparedStatement ps = connection.prepareStatement(insertionQry, Statement.RETURN_GENERATED_KEYS);
        ps.execute();
        ResultSet rs = ps.getGeneratedKeys();
        rs.first();
        long id = rs.getLong(1);

        Assert.assertEquals(true, id != 0);

        String loadQry = "select id, username, password, created, enabled, salt from users where id = " + id;

        ps = connection.prepareStatement(loadQry);
        rs = ps.executeQuery();

        rs.first();

        Assert.assertEquals(rs.getString("username"), "chris");
        Assert.assertEquals(rs.getString("password"), "somepassword");
        Assert.assertEquals(rs.getBoolean("enabled"), true);
        Assert.assertEquals(rs.getString("salt"), "salt");


        User user = new User("second_username", "newpassword", false, true, true, true, AuthorityUtils.NO_AUTHORITIES, "secondsalt");
        user.setId(rs.getLong("id"));

        userService.saveUser(user);

        ps = connection.prepareStatement(loadQry);
        rs = ps.executeQuery();

        rs.first();

        Assert.assertEquals(rs.getString("username"), "second_username");
        Assert.assertEquals(rs.getString("password"), "newpassword");
        Assert.assertEquals(rs.getBoolean("enabled"), false);
        Assert.assertEquals(rs.getString("salt"), "secondsalt");



    }

Ответы [ 2 ]

4 голосов
/ 04 октября 2011

Чтобы использовать необработанные JDBC Connection s с управлением транзакциями Spring, вам необходимо получить их как DataSourceUtils.getConnection(dataSource), см. DataSourceTransactionManagement. Возможно, в этом причина.

Итак, проблема в том, что Connection, полученный с помощью dataSource.getConnection() и используемый в тестовом коде, не совпадает с подключением, управляемым Spring, используемым в тестируемом коде. Таким образом, запросы, выполняемые в этих соединениях, принадлежат разным транзакциям, а выполнение запросов во многих транзакциях из одного потока часто приводит к тупикам.

При использовании DataSourceUtils вы получаете то же соединение, управляемое Spring, что и тестируемый код, так что все ваши запросы выполняются в одной транзакции.

0 голосов
/ 04 октября 2011

Я вижу несколько проблем с вашим тестированием:

  1. Ваши тесты тестируют много вещей одновременно.Вам нужно изучить изолированную (также известную как насмешливую) структуру, чтобы ваши тесты были более детальными.
  2. Выполнение тестов базы данных довольно сложно (у меня мало опыта в тестировании этого уровня).Возможно, вам лучше немного абстрагироваться, чтобы вы не использовали реальные ресурсы при выполнении тестов.Если вы обнаружите, что необходимо использовать реальные ресурсы, они должны быть предельно простыми и иметь чистую базу данных, с которой можно работать, чтобы избежать загрязнения данных ваших тестов.
  3. Никогда не повторяйте строки в ваших тестах, как в производственных целяхкод (то есть "Крис").Возможно, вам придется сделать ссылку в другом месте.В зависимости от среды тестирования вам может быть разрешено иметь базовый класс с общими объектами и т. Д., Который вы можете настроить по своему усмотрению.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...