Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
Топик располагается на нескольких страницах: Ctrl  назад   1 .. 13 14 15 16 17 18 19 [20] 21 22   вперед  Ctrl
 Re: unit-тестирование  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3531
Leonid Kudryavtsev
А если от реорганизации внутреннего кода, приходится менять тесты .... что эти тесты проверяют

так это же классика жарна для unit-тестов и моков: меняем код - разваливаются тесты.
4 мар 20, 14:17    [22092604]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
andreykaT
Member

Откуда:
Сообщений: 2720
Андрей Панфилов
Leonid Kudryavtsev
А если от реорганизации внутреннего кода, приходится менять тесты .... что эти тесты проверяют

так это же классика жарна для unit-тестов и моков: меняем код - разваливаются тесты.

ты о том что моки перестают отражать реальность?
4 мар 20, 14:36    [22092622]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Leonid Kudryavtsev
Member

Откуда:
Сообщений: 8475
Андрей Панфилов
Leonid Kudryavtsev
А если от реорганизации внутреннего кода, приходится менять тесты .... что эти тесты проверяют

так это же классика жарна для unit-тестов и моков: меняем код - разваливаются тесты.


на том проекте где я участвовал - как-то не особо разваливались

иногда разваливались, но достаточно редко

юнит тестами покрывали внешние интерфейсы и бизнес функциональность, внутренние классы и методы, типа getConnection(String dbConnectionString, String userName, String userPassword ) - покрывать юнит тестами "ума не хватило"

Андрей Панфилов

...
так это же классика жарна для unit-тестов и моков: меняем код - разваливаются тесты.

вот меня интересует:

а кто нибудь пишет юнит-тесты на тесты?

а то ведь не порядок! можно же в тестах ошибиться (((
4 мар 20, 14:37    [22092623]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Leonid Kudryavtsev
Member

Откуда:
Сообщений: 8475
попытаюсь пояснить свою мысль

если тест меняется так же часто как и код --> вероятность ошибки в тесте становится равна вероятности ошибки в коде --> если, а так скорее всего и есть, код и тест пишет один человек, вероятность того, что ошбка будет в обоих местах сразу = почти 99%

итог: смысл теста становится равный 0, т.к. тест становится просто еще одной copy/past переработанной версией кода. Тести проходят, покрытия кода 100%, а ошибок сколько было, столько и останется.

Вместо тестирования, получаем просто подгонку решения под заранее известный ответ. Это как в контрольной/домашнем_задании списывать ответы из конца учебника - вроде на все задачи дан правильный ответ, но знаний от этого не прибавится.
4 мар 20, 14:42    [22092627]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mayton
Member

Откуда: loopback
Сообщений: 45470
Leonid Kudryavtsev
asv79

вы видимо не занимались глубоким рефакторингом чужого кода и тесты к нему не писали

вот совершенно не понимаю, как __рефакторинг__ затрагивает тесты
и какие можно написать тесты К РЕФАКТОРИНГУ

Тесты должны корректность работы (функционирование) проверять. Как внутри организован код (рефакторинг) по определению на поведение тестов сказать не должно

Мы в топике стали говорить о разных вещах IMHO. Фаулер рекомендовал начинать рефакторинг ОСНОВНОГО кода
только при условии покрытия тестами. Не суть важно даже что там не 100%. Важно что рефакторинг не сломал
самые базовые контракты. Код компилируется успешно и бизнес-компоненты которые делают процессинг
вернули те-же самые статусы.

Сами тесты рефакторить не надо. К ним вообще нет требований кроме того что они состоят из аксиом и написаны
простым языком таблиц правил.

И если тесты стали вдруг сложны - то это (смотри пункт первый) что-то неверное в их дизайне.
4 мар 20, 14:52    [22092637]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Leonid Kudryavtsev
Member

Откуда:
Сообщений: 8475
Ровно о том же самом и говорим )))

mayton

Фаулер рекомендовал начинать рефакторинг ОСНОВНОГО кода
только при условии покрытия тестами. Не суть важно даже что там не 100%. Важно что рефакторинг не сломал
самые базовые контракты. Код компилируется успешно и бизнес-компоненты которые делают процессинг
вернули те-же самые статусы.


т.к. если
так это же классика жарна для unit-тестов и моков: меняем код - разваливаются тесты.

нафиг такие тесты нужны, лично мне не понятно

и как можно "развалившимися тестами" проверить. что "рефакторинг не сломал самые базовые контракты" мне тоже не понятно.
4 мар 20, 15:14    [22092664]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3531
andreykaT
ты о том что моки перестают отражать реальность?


Вот смотри. К примеру у нас есть класс Person:
public class Person {

   private Long id;

   private String name;

   public Person(Long id, String name) {
      this.id = id;
      this.name = name;
   }

}

К нему репозиторий:
public interface PersonRepository {
   
   Person getPersonById(Long id);

}

этот репозиторий куда-то инжектится, и то, куда оно инжекится, мы хотим покрыть unit-тестами, не долго думая мы пишем такой мок:
        when(personRepository.getPersonById(eq(1L)))
                .thenReturn(new Person(1L, "test name"));


Вроде все норм, но допустим, нам понадобилось в Person добавить новое поле - "gender", после чего у нас начинается свистопляски с моками, и нужно принимать решение что делать:
- по всему коду с моками вставлять другой вызов конструктора (а если полей несколько десятков?)
- перегружать конструктор - мне чет идея вообще не нравится
- сказать идея с конструктором изначально была так себе и нужно использовать паттерн builder - здесь получится так, что в половине тестовых сценариев объекты Person будут "полноценными", а в другой половине - нет

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

С другой стороны, если рассматривать все это дело с точки зрения интеграционных тестов, то добавление нового поля на старые тесты никакого влияния не оказывает, потому что вся кухня по наполнению поля скрыта под капотом используемого фреймворка, т.е. как только мы в проект затаскиваем моки, мы сразу же получаем кучу геморроя из ниоткуда.

Leonid Kudryavtsev
если тест меняется так же часто как и код --> вероятность ошибки в тесте становится равна вероятности ошибки в коде --> если, а так скорее всего и есть, код и тест пишет один человек, вероятность того, что ошбка будет в обоих местах сразу = почти 99%
совершенно верно.
4 мар 20, 15:14    [22092665]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5179
Leonid Kudryavtsev
попытаюсь пояснить свою мысль

если тест меняется так же часто как и код --> вероятность ошибки в тесте становится равна вероятности ошибки в коде --> если, а так скорее всего и есть, код и тест пишет один человек, вероятность того, что ошбка будет в обоих местах сразу = почти 99%

итог: смысл теста становится равный 0, т.к. тест становится просто еще одной copy/past переработанной версией кода. Тести проходят, покрытия кода 100%, а ошибок сколько было, столько и останется.

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


Если при изменении реализации тесты краснеют... Значит поменяли не реализацию, а контракт.
Если поменялся контракт, то естественно нужно менять тесты.
Когда при любых изменениях почти все тесты краснеют, это повод задуматься, что кто-то "наговнокодил" <:o)
5 мар 20, 06:09    [22093073]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5179
Андрей Панфилов
[
[/src]
К нему репозиторий:
public interface PersonRepository {
   
   Person getPersonById(Long id);

}

этот репозиторий куда-то инжектится, и то, куда оно инжекится, мы хотим покрыть unit-тестами, не долго думая мы пишем такой мок:


Репозиторий не куда-то инжектится, а инжектится в сервис DTO, который преобразует данные из Сущности Person, в конкретную модель Person для конкретного сервиса.

Соответственно, если в каком-то сервисе понадобилось деполнительное поле gender, то изменяется модель сервиса, DTO и Репозиторий.
Другие DTO и модели не менялись.

При изменении модели сервиса упадут тесты сервиса.
Потом DTO
И только потом репозитория.

Понятно, что удобнее репозиторий сразу вызывать из контроллера, минуя лишние слои.
Но все имеет свою цену.
5 мар 20, 06:19    [22093076]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Большой Синий Кит
Member

Откуда: Синий Океан
Сообщений: 1065
Ужас. Много шума из ничего...
5 мар 20, 11:45    [22093303]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mayton
Member

Откуда: loopback
Сообщений: 45470
Leonid Kudryavtsev

нафиг такие тесты нужны, лично мне не понятно

и как можно "развалившимися тестами" проверить. что "рефакторинг не сломал самые базовые контракты" мне тоже не понятно.

Если такие тесты "плохие" - то приведи пример пожалуйста хороших и правильных тестов.

Желательно с кодом.
5 мар 20, 12:27    [22093367]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3531
mad_nazgul
Репозиторий не куда-то инжектится, а инжектится в сервис DTO, который преобразует данные из Сущности Person, в конкретную модель Person для конкретного сервиса.
У вас, видимо, какая-то параллельная реальность: unit-тесты должны жить в том же модуле, который они покрывают, доменная модель - это отдельный модуль, и ничего про DTO он знать не знает, если у вас все понамешано в кучу, то мои соболезнования.
6 мар 20, 10:11    [22094238]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5179
Андрей Панфилов
mad_nazgul
Репозиторий не куда-то инжектится, а инжектится в сервис DTO, который преобразует данные из Сущности Person, в конкретную модель Person для конкретного сервиса.
У вас, видимо, какая-то параллельная реальность: unit-тесты должны жить в том же модуле, который они покрывают, доменная модель - это отдельный модуль, и ничего про DTO он знать не знает, если у вас все понамешано в кучу, то мои соболезнования.


Э-э-э почему доменная модель это отдельный модуль?!
Дядюшка Боб наоборот говорит, что для каждого сервиса/слоя должна быть своя модель с которой он работает.
DTO нужны для передачи данных между ними.
Т.е. для преобразования из одной модели в другую.

И все юнит-тесты находятся внутри своих серисов, и работают в рамках модели сервиса/слоя.

Т.е. при добавлении gender меняются тесты для конкретного сервиса он сипользуется, DTO к нему и репозитория.
Все остальные сервисы не должны затрагиваться.
Если это не так, то значит у вас сильная связанность. Спагетти-монолит.
Тронешь в одном месте, сломается где-то в другом.

В этом случае unit-тесты еще более необходимы.
Т.к. вылавливать ошибки в рантайме, то еще удвольствие.
6 мар 20, 11:05    [22094310]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5179
Leonid Kudryavtsev

вот меня интересует:

а кто нибудь пишет юнит-тесты на тесты?

а то ведь не порядок! можно же в тестах ошибиться (((


Зачем?

Логики в unit-тестах не должно быть.

У нас есть данные на входе, мы должны убедиться, что на выходе приходят правильные данные.

Если в unit-тестах есть логика, моки и пр...
То скорее всего это уже не unit-тест. Скорее всего это уже интеграционный тест.
6 мар 20, 11:10    [22094316]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
PetroNotC Sharp
Member

Откуда:
Сообщений: 4489
mad_nazgul,
Что значит Логика?
Она ведь везде есть.
Это раньше прогер писал битовые операции. И как головку в HDD прдвинуть.
6 мар 20, 11:12    [22094319]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3531
mad_nazgul
Э-э-э почему доменная модель это отдельный модуль?!
Дядюшка Боб наоборот говорит, что для каждого сервиса/слоя должна быть своя модель с которой он работает.
Мало ли что там вам упоротый дед вещает, доменная модель - это контракт на данные, она существует вне зависимости от сервисов более высокого уровня, т.е. если у меня есть задача типа "при изменении свойства A сущности B нужно выполнить операцию C", то эта задача должна быть реализована в модели, а не где-либо еще, в противном случае, если каждый модуль реализует свою собственную модель, то велика вероятность просрать контракт, обозначенный выше. Кроме этого, у самой модели зависимости минимальные, что в свою очередь означает, что ее можно переиспользовать везде.
6 мар 20, 11:28    [22094339]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5179
Андрей Панфилов
mad_nazgul
Э-э-э почему доменная модель это отдельный модуль?!
Дядюшка Боб наоборот говорит, что для каждого сервиса/слоя должна быть своя модель с которой он работает.
Мало ли что там вам упоротый дед вещает, доменная модель - это контракт на данные, она существует вне зависимости от сервисов более высокого уровня, т.е. если у меня есть задача типа "при изменении свойства A сущности B нужно выполнить операцию C", то эта задача должна быть реализована в модели, а не где-либо еще, в противном случае, если каждый модуль реализует свою собственную модель, то велика вероятность просрать контракт, обозначенный выше. Кроме этого, у самой модели зависимости минимальные, что в свою очередь означает, что ее можно переиспользовать везде.


Я про это и говорю. Unit-тест тестирует контракт.
И тестирует не то что вы говорите.

При таком значении А сущности B функция F должна вернуть сущность D с определенными значениями.
Причем это должно быть так, при любой фазе луны, настроения левой пятки и т.д.

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

А если на проекте потоптались несколько поколений программистов, то количество костылей, проверок на признаки и классы в методах зашкаливает.
6 мар 20, 12:00    [22094393]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3531
mad_nazgul
Я про это и говорю. Unit-тест тестирует контракт.
И тестирует не то что вы говорите.
unit-тест проверяет кусок кода, в идеале - метод, если он проверяет что-то большее, то это уже не unit-тест.

mad_nazgul
При таком значении А сущности B функция F должна вернуть сущность D с определенными значениями.
Причем это должно быть так, при любой фазе луны, настроения левой пятки и т.д.
Хорошо, сколько это будет стоить в итоге, если код нужно писать именно в таком стиле?

mad_nazgul
Насчет "переиспользования модели" как раз я всегда сталкиваюсь, что когда используют "общую модель" на несколько сервисов, там получается "спагетти монолит", где тронешь в одной части отвалиться в другой.
Еще раз: доменная модель самодостаточна, утрируя: наличие куя у человека - это свойство человека, а не какой-то сервис, находящийся непонятно где, который может быть в данный момент недоступен, проецируя же это на реальность, лично я бы предпочел, лечиться у врачей, у которых есть полный доступ к моей медицинской истории, а не где есть доступ только к ограниченному количеству информации
6 мар 20, 12:25    [22094437]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mad_nazgul
Member

Откуда:
Сообщений: 5179
Андрей Панфилов
mad_nazgul
Я про это и говорю. Unit-тест тестирует контракт.
И тестирует не то что вы говорите.
unit-тест проверяет кусок кода, в идеале - метод, если он проверяет что-то большее, то это уже не unit-тест.


unit-тест проверяет не кусок кода, а контракт.
Т.е. что-то подав на вход, мы что-то получим на выходе.
В т.ч. контракт на ошибки/исключения
Т.е. мы считаем, что у нас "черный ящик" и ждем от него определенного поведения.

Андрей Панфилов

mad_nazgul
При таком значении А сущности B функция F должна вернуть сущность D с определенными значениями.
Причем это должно быть так, при любой фазе луны, настроения левой пятки и т.д.
Хорошо, сколько это будет стоить в итоге, если код нужно писать именно в таком стиле?


Дорого. А вот изменения данного кода будут стоит гораздо дешевле, чем изменения кода без тестов.
Принцип "Лучше день потерять, потом за пят минут долететь"
Т.е. лучше потратиться на начальном этапе на написание тестов и на чистоту кода, чем потом тратить кучу времени на тестирование регрессий.

Андрей Панфилов

mad_nazgul
Насчет "переиспользования модели" как раз я всегда сталкиваюсь, что когда используют "общую модель" на несколько сервисов, там получается "спагетти монолит", где тронешь в одной части отвалиться в другой.
Еще раз: доменная модель самодостаточна, утрируя: наличие куя у человека - это свойство человека, а не какой-то сервис, находящийся непонятно где, который может быть в данный момент недоступен, проецируя же это на реальность, лично я бы предпочел, лечиться у врачей, у которых есть полный доступ к моей медицинской истории, а не где есть доступ только к ограниченному количеству информации


Вообще-то вся медицинская история нужна только одному врачу - терапевту, который ставит диагноз.
А вот лечение и более подробный диагноз ставят специализированные врачи.
И они смотрят на конкретные анализы, в рамках своей компетенции.
Но это аналогия. Которая к обсуждаемому вопросу имеет отношении менее чем никакое.

Я могу привести аналогию с секретными документами.
Что там как раз нужно выдавать информацию не более чем, она нужна для работы.

А так "Универсал, это тот кто делает все одинаково плохо".
Универсальную доменную модель можно наверное построить, но работать с ней будет очень не удобно.
6 мар 20, 14:16    [22094610]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
istrebitel
Member

Откуда:
Сообщений: 49
mayton

Если такие тесты "плохие" - то приведи пример пожалуйста хороших и правильных тестов.

Желательно с кодом.

http://jtds.sourceforge.net
+
// jTDS JDBC Driver for Microsoft SQL Server and Sybase
// Copyright (C) 2004 The jTDS Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
package net.sourceforge.jtds.jdbc;

import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;

import junit.framework.AssertionFailedError;
//
// MJH - Changes for new jTDS version
// Added registerOutParameter to testCallableStatementParsing2
//
/**
 * @version 1.0
 */
public class CallableStatementTest extends TestBase {

    /** set to false to enable verbose console output */
    private final boolean SILENT = true;

    public CallableStatementTest(String name) {
        super(name);
    }

   /**
    * Test comment processing, bug #634 (and #676).
    */
   public void testCommentProcessing()
      throws SQLException
   {
      Statement st = con.createStatement();
      st.executeUpdate( "create procedure #sp_bug634 @data1 int, @data2 int as select @data1 + @data2" );
      st.close();

      String[] variants = new String[]
      {
         "{?=call #sp_bug634(?, ?)}",

         "/*/ comment '\"?@[*-} /**/*/?=call #sp_bug634(?, ?)",
         "?/*/ comment '\"?@[*-} /**/*/=call #sp_bug634(?, ?)",
         "?/*/ comment '\"?@[*-} /**/*/=call #sp_bug634(?, ?)",
         "?=/*/ comment '\"?@[*-} /**/*/call #sp_bug634(?, ?)",
         "?=call /*/ comment '\"?@[*-} /**/*/#sp_bug634(?, ?)",
         "?=call #sp_bug634/*/ comment '\"?@[*-} /**/*/(?, ?)",
         "?=call #sp_bug634(/*/ comment '\"?@[*-} /**/*/?, ?)",
         "?=call #sp_bug634(?/*/ comment '\"?@[*-} /**/*/, ?)",
         "?=call #sp_bug634(?,/*/ comment '\"?@[*-} /**/*/ ?)",
         "?=call #sp_bug634(?, ?/*/ comment '\"?@[*-} /**/*/)",
         "?=call #sp_bug634(?, ?)/*/ comment '\"?@[*-} /**/*/",
         "?=call #sp_bug634(?, ?)/*/ comment '\"?@[*-} /**/*/",
         "?=call #sp_bug634(?, ?) -- comment '\"?@[*-",
         "?=call -- comment '\"?@[*-}\n #sp_bug634(?, ?)",
         "?=call #sp_bug634(-- comment '\"?@[*-}\n ?, ?)",

         "/*/ comment '\"?@[*-} /**/*/{?=call #sp_bug634(?, ?)}",
         "{/*/ comment '\"?@[*-} /**/*/?=call #sp_bug634(?, ?)}",
         "{?/*/ comment '\"?@[*-} /**/*/=call #sp_bug634(?, ?)}",
         "{?=/*/ comment '\"?@[*-} /**/*/call #sp_bug634(?, ?)}",
         "{?=call /*/ comment '\"?@[*-} /**/*/#sp_bug634(?, ?)}",
         "{?=call #sp_bug634/*/ comment '\"?@[*-} /**/*/(?, ?)}",
         "{?=call #sp_bug634(/*/ comment '\"?@[*-} /**/*/?, ?)}",
         "{?=call #sp_bug634(?/*/ comment '\"?@[*-} /**/*/, ?)}",
         "{?=call #sp_bug634(?,/*/ comment '\"?@[*-} /**/*/ ?)}",
         "{?=call #sp_bug634(?, ?/*/ comment '\"?@[*-} /**/*/)}",
         "{?=call #sp_bug634(?, ?)/*/ comment '\"?@[*-} /**/*/}",
         "{?=call #sp_bug634(?, ?)}/*/ comment '\"?@[*-} /**/*/",
         "{?=call #sp_bug634(?, ?)} -- comment '\"?@[*-}",
         "{?=call -- comment '\"?@[*-}\n #sp_bug634(?, ?)}",
         "{?=call #sp_bug634(-- comment '\"?@[*-}\n ?, ?)}"
      };

      for( int i = 0; i < variants.length;  i ++ )
      {
         CallableStatement cst = null;
         ResultSet         res = null;

         try
         {
            cst = con.prepareCall( variants[i] );
            cst.registerOutParameter( 1, Types.INTEGER );
            cst.setInt( 2, i );
            cst.setInt( 3, i );
            res = cst.executeQuery();

            assertTrue  ( res.next()             );
            assertEquals( 2 * i, res.getInt( 1 ) );
            assertFalse ( res.next()             );
         }
         catch( SQLException e )
         {
            AssertionFailedError error = new AssertionFailedError( "variant \"" + variants[i] + "\" failed: " + e.getMessage() );
            error.initCause( e );
            throw error;
         }
         finally
         {
            if( res != null ) res.close();
            if( cst != null ) cst.close();
         }
      }
   }

    public void testCallableStatement() throws Exception {
        CallableStatement cstmt = con.prepareCall("{call sp_who}");

        cstmt.close();
    }

    public void testCallableStatement1() throws Exception {
        CallableStatement cstmt = con.prepareCall("sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump(rs,SILENT);

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementCall1() throws Exception {
        CallableStatement cstmt = con.prepareCall("{call sp_who}");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementCall2() throws Exception {
        CallableStatement cstmt = con.prepareCall("{CALL sp_who}");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementCall3() throws Exception {
        CallableStatement cstmt = con.prepareCall("{cAlL sp_who}");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    /**
     * Test for bug [974801] stored procedure error in Northwind
     */
    public void testCallableStatementCall4() throws Exception {
        Statement stmt;

        try {
            stmt = con.createStatement();
            stmt.execute("create procedure \"#test space\" as SELECT COUNT(*) FROM sysobjects");
            stmt.close();

            CallableStatement cstmt = con.prepareCall("{call \"#test space\"}");

            ResultSet rs = cstmt.executeQuery();
            dump( rs,SILENT );

            rs.close();
            cstmt.close();
        } finally {
            stmt = con.createStatement();
            stmt.execute("drop procedure \"#test space\"");
            stmt.close();
        }
    }

    public void testCallableStatementExec1() throws Exception {
        CallableStatement cstmt = con.prepareCall("exec sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec2() throws Exception {
        CallableStatement cstmt = con.prepareCall("EXEC sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec3() throws Exception {
        CallableStatement cstmt = con.prepareCall("execute sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec4() throws Exception {
        CallableStatement cstmt = con.prepareCall("EXECUTE sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec5() throws Exception {
        CallableStatement cstmt = con.prepareCall("eXeC sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec6() throws Exception {
        CallableStatement cstmt = con.prepareCall("ExEcUtE sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec7() throws Exception {
        CallableStatement cstmt = con.prepareCall("execute \"master\"..sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec8() throws Exception {
        Statement stmt;

        try {
            stmt = con.createStatement();
            stmt.execute("create procedure #test as SELECT COUNT(*) FROM sysobjects");
            stmt.close();

            CallableStatement cstmt = con.prepareCall("execute #test");

            ResultSet rs = cstmt.executeQuery();
            dump( rs,SILENT );

            rs.close();
            cstmt.close();
        } finally {
            stmt = con.createStatement();
            stmt.execute("drop procedure #test");
            stmt.close();
        }
    }

    /**
     * Test for bug [978175] 0.8: Stored Procedure call doesn't work anymore
     */
    public void testCallableStatementExec9() throws Exception {
        CallableStatement cstmt = con.prepareCall("{call sp_who}");

        assertTrue(cstmt.execute());

        ResultSet rs = cstmt.getResultSet();

        if (rs == null) {
            fail("Null ResultSet returned");
        } else {
            dump( rs,SILENT );
            rs.close();
        }

        cstmt.close();
    }

    public void testCallableStatementParsing1() throws Exception {
        String data = "New {order} plus {1} more";
        Statement stmt = con.createStatement();

        stmt.execute("CREATE TABLE #csp1 (data VARCHAR(32))");
        stmt.close();

        stmt = con.createStatement();
        stmt.execute("create procedure #sp_csp1 @data VARCHAR(32) as INSERT INTO #csp1 (data) VALUES(@data)");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #sp_csp1(?)}");

        cstmt.setString(1, data);
        cstmt.execute();
        cstmt.close();

        stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT data FROM #csp1");

        assertTrue(rs.next());

        assertTrue(data.equals(rs.getString(1)));

        assertTrue(!rs.next());
        rs.close();
        stmt.close();
    }

    /**
     * Test for bug [938632] String index out of bounds error in 0.8rc1.
     */
    public void testCallableStatementParsing2() throws Exception {
        try {
            Statement stmt = con.createStatement();

            stmt.execute("create procedure #load_smtp_in_1gr_ls804192 as SELECT name FROM sysobjects");
            stmt.close();

            CallableStatement cstmt = con.prepareCall("{?=call #load_smtp_in_1gr_ls804192}");
            cstmt.registerOutParameter(1, java.sql.Types.INTEGER); // MJH 01/05/04
            cstmt.execute();
            cstmt.close();
        } finally {
            Statement stmt = con.createStatement();

            stmt.execute("drop procedure #load_smtp_in_1gr_ls804192");
            stmt.close();
        }
    }

    /**
     * Test for bug [1006845] Stored procedure with 18 parameters.
     */
    public void testCallableStatementParsing3() throws Exception {
        CallableStatement cstmt = con.prepareCall("{Call Test(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}");
        cstmt.close();
    }

    /**
     * Test for incorrect exception thrown/no exception thrown when invalid
     * call escape is used.
     * <p/>
     * See https://sourceforge.net/forum/forum.php?thread_id=1144619&forum_id=104389
     * for more detail.
     */
    public void testCallableStatementParsing4() throws SQLException {
        try {
            con.prepareCall("{call ? = sp_create_employee (?, ?, ?, ?, ?, ?)}");
            fail("Was expecting an invalid escape sequence error");
        } catch (SQLException ex) {
            assertEquals("22025", ex.getSQLState());
        }
    }

    /**
     * Test for bug [1052942] Error processing JDBC call escape. (A blank
     * before the final <code>}</code> causes the parser to fail).
     */
    public void testCallableStatementParsing5() throws Exception {
        CallableStatement cstmt = con.prepareCall(" { Call Test(?,?) } ");
        cstmt.close();
    }

    /**
     * Test for incorrect exception thrown/no exception thrown when invalid
     * call escape is used.
     * <p/>
     * A message containing the correct missing terminator should be generated.
     */
    public void testCallableStatementParsing6() throws SQLException {
        try {
            con.prepareCall("{call sp_test(?, ?)");
            fail("Was expecting an invalid escape error");
        } catch (SQLException ex) {
            assertEquals("22025", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf('}') != -1);
        }
    }

    /**
     * Test for incorrect exception thrown/no exception thrown when invalid
     * call escape is used.
     * <p/>
     * A message containing the correct missing terminator should be generated.
     */
    public void testCallableStatementParsing7() throws SQLException {
        try {
            con.prepareCall("{call sp_test(?, ?}");
            fail("Was expecting an invalid escape error");
        } catch (SQLException ex) {
            assertEquals("22025", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf(')') != -1);
        }
    }

    /**
     * Test for reature request [956800] setNull(): Not implemented.
     */
    public void testCallableSetNull1() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("CREATE TABLE #callablesetnull1 (data CHAR(1) NULL)");
        stmt.close();

        try {
            stmt = con.createStatement();
            stmt.execute("create procedure #procCallableSetNull1 @data char(1) "
                     + "as INSERT INTO #callablesetnull1 (data) VALUES (@data)");
            stmt.close();

            CallableStatement cstmt = con.prepareCall("{call #procCallableSetNull1(?)}");
            // Test CallableStatement.setNull(int,Types.NULL)
            cstmt.setNull(1, Types.NULL);
            cstmt.execute();
            cstmt.close();

            stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT data FROM #callablesetnull1");

            assertTrue(rs.next());

            // Test ResultSet.getString()
            assertNull(rs.getString(1));
            assertTrue(rs.wasNull());

            assertTrue(!rs.next());
            stmt.close();
            rs.close();
        } finally {
            stmt = con.createStatement();
            stmt.execute("drop procedure #procCallableSetNull1");
            stmt.close();
        }
    }

    /**
     * Test for bug [974284] retval on callable statement isn't handled correctly
     */
    public void testCallableRegisterOutParameter1() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("create procedure #rop1 @a varchar(1), @b varchar(1) as\r\n "
                     + "begin\r\n"
                     + "return 1\r\n"
                     + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{? = call #rop1(?, ?)}");

        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.setString(2, "a");
        cstmt.setString(3, "b");
        cstmt.execute();

        assertEquals(1, cstmt.getInt(1));
        assertEquals("1", cstmt.getString(1));

        cstmt.close();
    }

    /**
     * Test for bug [994888] Callable statement and Float output parameter
     */
    public void testCallableRegisterOutParameter2() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("create procedure #rop2 @data float OUTPUT as\r\n "
                     + "begin\r\n"
                     + "set @data = 1.1\r\n"
                     + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #rop2(?)}");

        cstmt.registerOutParameter(1, Types.FLOAT);
        cstmt.execute();

        assertTrue(cstmt.getFloat(1) == 1.1f);
        cstmt.close();
    }

    /**
     * Test for bug [994988] Network error when null is returned via int output parm
     */
    public void testCallableRegisterOutParameter3() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("create procedure #rop3 @data int OUTPUT as\r\n "
                     + "begin\r\n"
                     + "set @data = null\r\n"
                     + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #rop3(?)}");

        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.execute();

        cstmt.getInt(1);
        assertTrue(cstmt.wasNull());
        cstmt.close();
    }

   /**
    * Test for bug [983432] Prepared call doesn't work with jTDS 0.8
    */
   public void testCallableRegisterOutParameter4()
      throws Exception
   {
      // cleanup remains from last run
      dropProcedure( "rop4" );
      dropType( "T_INTEGER" );

      CallableStatement cstmt = con.prepareCall( "{call sp_addtype T_INTEGER, int, 'NULL'}" );
      Statement stmt = con.createStatement();

      try
      {
         cstmt.execute();
         cstmt.close();

         stmt.execute( "create procedure rop4 @data T_INTEGER OUTPUT as\r\n " + "begin\r\n" + "set @data = 1\r\n" + "end" );
         stmt.close();

         cstmt = con.prepareCall( "{call rop4(?)}" );

         cstmt.registerOutParameter( 1, Types.VARCHAR );
         cstmt.execute();

         assertEquals( cstmt.getInt( 1 ), 1 );
         assertTrue( !cstmt.wasNull() );
         cstmt.close();

         cstmt = con.prepareCall( "rop4 ?" );

         cstmt.registerOutParameter( 1, Types.VARCHAR );
         cstmt.execute();

         assertEquals( cstmt.getInt( 1 ), 1 );
         assertTrue( !cstmt.wasNull() );
         cstmt.close();
      }
      finally
      {
         // cleanup
         dropProcedure( "rop4" );
         dropType( "T_INTEGER" );
      }
   }

   /**
    * Test for bug [946171] null boolean in CallableStatement bug
    */
   public void testCallableRegisterOutParameter5() throws Exception {
       Statement stmt = con.createStatement();
       stmt.execute("create procedure #rop1 @bool bit, @whatever int OUTPUT as\r\n "
                    + "begin\r\n"
                    + "set @whatever = 1\r\n"
                    + "end");
       stmt.close();

       try {
           CallableStatement cstmt = con.prepareCall("{call #rop1(?,?)}");

           cstmt.setNull(1, Types.BOOLEAN);
           cstmt.registerOutParameter(2, Types.INTEGER);
           cstmt.execute();

           assertTrue(cstmt.getInt(2) == 1);
           cstmt.close();
       } finally {
           stmt = con.createStatement();
           stmt.execute("drop procedure #rop1");
           stmt.close();
       }
   }

   /**
    * Test for bug [992715] wasnull() always returns false
    */
   public void testCallableRegisterOutParameter6() throws Exception {
       Statement stmt = con.createStatement();
       stmt.execute("create procedure #rop2 @bool bit, @whatever varchar(1) OUTPUT as\r\n "
                    + "begin\r\n"
                    + "set @whatever = null\r\n"
                    + "end");
       stmt.close();

       CallableStatement cstmt = con.prepareCall("{call #rop2(?,?)}");

       cstmt.setNull(1, Types.BOOLEAN);
       cstmt.registerOutParameter(2, Types.VARCHAR);
       cstmt.execute();

       assertTrue(cstmt.getString(2) == null);
       assertTrue(cstmt.wasNull());
       cstmt.close();
   }

    /**
     * Test for bug [991640] java.sql.Date error and RAISERROR problem
     */
    public void testCallableError1() throws Exception {
        String text = "test message";

        Statement stmt = con.createStatement();
        stmt.execute("create procedure #ce1 as\r\n "
                     + "begin\r\n"
                     + "RAISERROR('" + text + "', 16, 1 )\r\n"
                     + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #ce1}");

        try {
            cstmt.execute();
            assertTrue(false);
        } catch (SQLException e) {
            assertTrue(e.getMessage().equals(text));
        }

        cstmt.close();
    }

   /**
    * Test named parameters.
    */
   public void testNamedParameters0001()
      throws Exception
   {
      final String data = "New {order} plus {1} more";
      final String outData = "test";

      Statement stmt = con.createStatement();

      stmt.execute( "CREATE TABLE #csn1 ( data VARCHAR(32) )" );
      stmt.execute( "create procedure #sp_csn1 @data VARCHAR(32) OUT as INSERT INTO #csn1 (data) VALUES(@data) SET @data = '" + outData + "'" + "RETURN 13" );

      CallableStatement cstmt = con.prepareCall( "{?=call #sp_csn1(?)}" );

      cstmt.registerOutParameter( "@return_status", Types.INTEGER );
      cstmt.setString( "@data", data );
      cstmt.registerOutParameter( "@data", Types.VARCHAR );
      assertEquals( 1, cstmt.executeUpdate() );
      assertFalse( cstmt.getMoreResults() );
      assertEquals( -1, cstmt.getUpdateCount() );
      assertEquals( outData, cstmt.getString( "@data" ) );
      cstmt.close();

      ResultSet rs = stmt.executeQuery( "SELECT data FROM #csn1" );

      assertTrue( rs.next() );
      assertEquals( data, rs.getString( 1 ) );
      assertTrue( ! rs.next() );

      rs.close();
      stmt.close();
   }

   /**
    * Test named parameters.
    */
   public void testNamedParameters0002()
      throws Exception
   {
      final String  A_DEFAULT = "XYZ";
      final Integer B_DEFAULT = 123;
      final Integer C_DEFAULT = 321;

      Statement stmt = con.createStatement();
      stmt.execute( "create table #Test ( A varchar(10), B int, C int, D int primary key )" );
      stmt.execute( "create procedure #spInsert @A_VAL varchar(10) = " + A_DEFAULT + " out, @B_VAL int = " + B_DEFAULT + ", @C_VAL int = " + C_DEFAULT + " out, @D_VAL int as INSERT INTO #Test VALUES( @A_VAL, @B_VAL, @C_VAL, @D_VAL ) set @A_VAL = 'RET' set @C_VAL = @B_VAL + @C_VAL return @B_VAL" );

      CallableStatement cstmt = con.prepareCall( "{?=call #spInsert(?, ?, ?, ?)}" );

      cstmt.registerOutParameter( 1, Types.INTEGER );
      cstmt.registerOutParameter( "A_VAL", Types.VARCHAR );
      cstmt.registerOutParameter( "C_VAL", Types.INTEGER );

      cstmt.setObject( "A_VAL", A_DEFAULT );
      cstmt.setObject( "B_VAL", B_DEFAULT );
      cstmt.setObject( "C_VAL", C_DEFAULT );
      cstmt.setInt   ( "D_VAL", 0         );

      assertEquals( 1, cstmt.executeUpdate() );
      assertFalse( cstmt.getMoreResults() );
      assertEquals( -1, cstmt.getUpdateCount() );

      assertEquals( B_DEFAULT, cstmt.getObject( 1 ) );
      assertEquals( "RET", cstmt.getObject( "A_VAL" ) );
      assertEquals( B_DEFAULT + C_DEFAULT, cstmt.getObject( "C_VAL" ) );
      cstmt.close();

      ResultSet rs = stmt.executeQuery( "select A, B, C from #Test where D = 0" );

      assertTrue( rs.next() );
      assertEquals( A_DEFAULT, rs.getObject( "A" ) );
      assertEquals( B_DEFAULT, rs.getObject( "B" ) );
      assertEquals( C_DEFAULT, rs.getObject( "C" ) );
      assertTrue( ! rs.next() );

      rs.close();

      // and once again without setting all parameters

      cstmt = con.prepareCall( "{?=call #spInsert(?,?)}" );

      cstmt.registerOutParameter( 1, Types.INTEGER );

      cstmt.setInt( "B_VAL", 9876 );
      cstmt.setInt( "D_VAL", 1    );

      assertEquals( 1, cstmt.executeUpdate() );
      assertFalse( cstmt.getMoreResults() );
      assertEquals( -1, cstmt.getUpdateCount() );

      assertEquals( 9876, cstmt.getObject( 1 ) );
      cstmt.close();

      rs = stmt.executeQuery( "select A, B, C from #Test where D = 1" );

      assertTrue( rs.next() );
      assertEquals( A_DEFAULT, rs.getObject( "A" ) );
      assertEquals( 9876     , rs.getObject( "B" ) );
      assertEquals( C_DEFAULT, rs.getObject( "C" ) );
      assertTrue( ! rs.next() );

      rs.close();
      stmt.close();
   }

   /**
     * Test that procedure outputs are available immediately for procedures
     * that do not return ResultSets (i.e that update counts are cached).
     */
    public void testProcessUpdateCounts1() throws SQLException {
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testProcessUpdateCounts1 (val INT)"));
        assertFalse(stmt.execute("CREATE PROCEDURE #procTestProcessUpdateCounts1"
                + " @res INT OUT AS"
                + " INSERT INTO #testProcessUpdateCounts1 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts1 SET val = 2"
                + " INSERT INTO #testProcessUpdateCounts1 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts1 SET val = 3"
                + " SET @res = 13"
                + " RETURN 14"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall(
                "{?=call #procTestProcessUpdateCounts1(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.registerOutParameter(2, Types.INTEGER);

        assertFalse(cstmt.execute());
        assertEquals(14, cstmt.getInt(1));
        assertEquals(13, cstmt.getInt(2));

        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(2, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(-1, cstmt.getUpdateCount());

        cstmt.close();
    }

    /**
     * Test that procedure outputs are available immediately after processing
     * the last ResultSet returned by the procedure (i.e that update counts
     * are cached).
     */
    public void testProcessUpdateCounts2() throws SQLException {
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testProcessUpdateCounts2 (val INT)"));
        assertFalse(stmt.execute("CREATE PROCEDURE #procTestProcessUpdateCounts2"
                + " @res INT OUT AS"
                + " INSERT INTO #testProcessUpdateCounts2 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts2 SET val = 2"
                + " SELECT * FROM #testProcessUpdateCounts2"
                + " INSERT INTO #testProcessUpdateCounts2 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts2 SET val = 3"
                + " SET @res = 13"
                + " RETURN 14"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall(
                "{?=call #procTestProcessUpdateCounts2(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.registerOutParameter(2, Types.INTEGER);

        assertFalse(cstmt.execute());
        try {
            assertEquals(14, cstmt.getInt(1));
            assertEquals(13, cstmt.getInt(2));
            // Don't fail the test if we got here. Another driver or a future
            // version could cache all the results and obtain the output
            // parameter values from the beginning.
        } catch (SQLException ex) {
            assertEquals("HY010", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf("getMoreResults()") >= 0);
        }

        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // UPDATE

        assertTrue(cstmt.getMoreResults()); // SELECT

        assertFalse(cstmt.getMoreResults());
        assertEquals(14, cstmt.getInt(1));
        assertEquals(13, cstmt.getInt(2));
        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(2, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(-1, cstmt.getUpdateCount());

        cstmt.close();
    }

    /**
     * Test that procedure outputs are available immediately after processing
     * the last ResultSet returned by the procedure (i.e that update counts
     * are cached) even if getMoreResults() is not called.
     */
    public void testProcessUpdateCounts3() throws SQLException {
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testProcessUpdateCounts3 (val INT)"));
        assertFalse(stmt.execute("CREATE PROCEDURE #procTestProcessUpdateCounts3"
                + " @res INT OUT AS"
                + " INSERT INTO #testProcessUpdateCounts3 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts3 SET val = 2"
                + " SELECT * FROM #testProcessUpdateCounts3"
                + " INSERT INTO #testProcessUpdateCounts3 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts3 SET val = 3"
                + " SET @res = 13"
                + " RETURN 14"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall(
                "{?=call #procTestProcessUpdateCounts3(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.registerOutParameter(2, Types.INTEGER);

        assertFalse(cstmt.execute());
        try {
            assertEquals(14, cstmt.getInt(1));
            assertEquals(13, cstmt.getInt(2));
            // Don't fail the test if we got here. Another driver or a future
            // version could cache all the results and obtain the output
            // parameter values from the beginning.
        } catch (SQLException ex) {
            assertEquals("HY010", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf("getMoreResults()") >= 0);
        }

        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // UPDATE

        assertTrue(cstmt.getMoreResults()); // SELECT
        ResultSet rs = cstmt.getResultSet();
        assertNotNull(rs);
        // Close the ResultSet; this should cache the following update counts
        rs.close();

        assertEquals(14, cstmt.getInt(1));
        assertEquals(13, cstmt.getInt(2));

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(2, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(-1, cstmt.getUpdateCount());

        cstmt.close();
    }

    /**
     * Test that procedure outputs are available immediately after processing
     * the last ResultSet returned by the procedure (i.e that update counts
     * are cached) even if getMoreResults() and ResultSet.close() are not
     * called.
     */
    public void testProcessUpdateCounts4() throws SQLException {
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testProcessUpdateCounts4 (val INT)"));
        assertFalse(stmt.execute("CREATE PROCEDURE #procTestProcessUpdateCounts4"
                + " @res INT OUT AS"
                + " INSERT INTO #testProcessUpdateCounts4 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts4 SET val = 2"
                + " SELECT * FROM #testProcessUpdateCounts4"
                + " INSERT INTO #testProcessUpdateCounts4 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts4 SET val = 3"
                + " SET @res = 13"
                + " RETURN 14"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall(
                "{?=call #procTestProcessUpdateCounts4(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.registerOutParameter(2, Types.INTEGER);

        assertFalse(cstmt.execute());
        try {
            assertEquals(14, cstmt.getInt(1));
            assertEquals(13, cstmt.getInt(2));
            // Don't fail the test if we got here. Another driver or a future
            // version could cache all the results and obtain the output
            // parameter values from the beginning.
        } catch (SQLException ex) {
            assertEquals("HY010", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf("getMoreResults()") >= 0);
        }

        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // UPDATE

        assertTrue(cstmt.getMoreResults()); // SELECT
        ResultSet rs = cstmt.getResultSet();
        assertNotNull(rs);
        // Process all rows; this should cache the following update counts
        assertTrue(rs.next());
        assertFalse(rs.next());

        assertEquals(14, cstmt.getInt(1));
        assertEquals(13, cstmt.getInt(2));

        // Only close the ResultSet now
        rs.close();

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(2, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(-1, cstmt.getUpdateCount());

        cstmt.close();
    }

    /**
     * Test for bug [ 1062671 ] SQLParser unable to parse CONVERT(char,{ts ?},102)
     */
    public void testTsEscape() throws Exception {
        Timestamp ts = Timestamp.valueOf("2004-01-01 23:56:56");
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testTsEscape (val DATETIME)"));
        PreparedStatement pstmt = con.prepareStatement("INSERT INTO #testTsEscape VALUES({ts ?})");
        pstmt.setTimestamp(1, ts);
        assertEquals(1, pstmt.executeUpdate());
        ResultSet rs = stmt.executeQuery("SELECT * FROM #testTsEscape");
        assertTrue(rs.next());
        assertEquals(ts, rs.getTimestamp(1));
    }

    /**
     * Test for separation of IN and INOUT/OUT parameter values
     */
    public void testInOutParameters() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("CREATE PROC #testInOut @in int, @out int output as SELECT @out = @out + @in");
        CallableStatement cstmt = con.prepareCall("{ call #testInOut ( ?,? ) }");
        cstmt.setInt(1, 1);
        cstmt.registerOutParameter(2, Types.INTEGER);
        cstmt.setInt(2, 2);
        cstmt.execute();
        assertEquals(3, cstmt.getInt(2));
        cstmt.execute();
        assertEquals(3, cstmt.getInt(2));
    }

    /**
     * Test that procedure names containing semicolons are parsed correctly.
     */
    public void testSemicolonProcedures() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("CREATE PROC #testInOut @in int, @out int output as SELECT @out = @out + @in");
        CallableStatement cstmt = con.prepareCall("{call #testInOut;1(?,?)}");
        cstmt.setInt(1, 1);
        cstmt.registerOutParameter(2, Types.INTEGER);
        cstmt.setInt(2, 2);
        cstmt.execute();
        assertEquals(3, cstmt.getInt(2));
        cstmt.execute();
        assertEquals(3, cstmt.getInt(2));
    }

    /**
     * Test that procedure calls with both literal parameters and parameterr
     * markers are executed correctly (bug [1078927] Callable statement fails).
     */
    public void testNonRpcProc1() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute(
                "create proc #testsp1 @p1 int, @p2 int out as set @p2 = @p1");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #testsp1(100, ?)}");
        cstmt.setInt(1, 1);
        cstmt.execute();
        cstmt.close();
    }

    /**
     * Test that procedure calls with both literal parameters and parameterr
     * markers are executed correctly (bug [1078927] Callable statement fails).
     */
    public void testNonRpcProc2() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("create proc #testsp2 @p1 int, @p2 int as return 99");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{?=call #testsp2(100, ?)}");
        cstmt.registerOutParameter(1, java.sql.Types.INTEGER);
        cstmt.setInt(2, 2);
        cstmt.execute();
        assertEquals(99, cstmt.getInt(1));
        cstmt.close();
    }

    /**
     * Test for bug [1152329] Spurious output params assigned (TIMESTMP).
     * <p/>
     * If a stored procedure execute WRITETEXT or UPDATETEXT commands, spurious
     * output parameter data is returned to the client. This additional data
     * can be confused with the real output parameter data leading to an output
     * string parameter returning the text ?TIMESTMP? on SQL Server 7+ or
     * binary garbage on other servers.
     */
    public void testWritetext() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute(
                "create proc #testWritetext @p1 varchar(20) output as "
                + "begin "
                + "create table #test (id int, txt text) "
                + "insert into #test (id, txt) values(1, '') "
                + "declare @ptr binary(16) "
                + "select @ptr = (select textptr(txt) from #test where id = 1) "
                + "writetext #test.txt @ptr 'This is a test' "
                + "select @p1 = 'done' "
                + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #testWritetext(?)}");
        cstmt.registerOutParameter(1, Types.VARCHAR);
        cstmt.execute();
        assertEquals("done", cstmt.getString(1));
        cstmt.close();
    }

    /**
     * Test for bug [1047208] SQLException chaining not implemented correctly:
     * checks that all errors are returned and that output variables are also
     * returned.
     */
    public void testErrorOutputParams() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("CREATE PROC #error_proc @p1 int out AS \r\n" +
                     "RAISERROR ('TEST EXCEPTION', 15, 1)\r\n" +
                     "SELECT @P1=100\r\n" +
                     "CREATE TABLE #DUMMY (id int)\r\n" +
                     "INSERT INTO #DUMMY VALUES(1)\r\n"+
                     "INSERT INTO #DUMMY VALUES(1)");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #error_proc(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        try {
            cstmt.execute();
            fail("Expecting exception");
        } catch (SQLException e) {
            assertEquals("TEST EXCEPTION", e.getMessage());
        }
        assertEquals(100, cstmt.getInt(1));
        cstmt.close();
    }

    /**
     * Test for bug [1236078] Procedure doesn't get called for some BigDecimal
     * values - invalid bug.
     */
    public void testBigDecimal() throws Exception {
        Statement stmt = con.createStatement();
        assertEquals(0, stmt.executeUpdate("CREATE TABLE #dec_test "
                + "(ColumnVC varchar(50) NULL, ColumnDec decimal(18,4) NULL)"));
        assertEquals(0, stmt.executeUpdate("CREATE PROCEDURE #dec_test2"
                + "(@inVc varchar(32), @inBd decimal(18,4)) AS "
                + "begin "
                + "update #dec_test set columnvc = @inVc, columndec = @inBd "
                + "end"));
        assertEquals(1, stmt.executeUpdate(
                "insert #dec_test (columnvc, columndec) values (null, null)"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #dec_test2 (?,?)}");
        cstmt.setString(1, "D: " + new java.util.Date());
        cstmt.setBigDecimal(2, new BigDecimal("2.9E+7"));
        assertEquals(1, cstmt.executeUpdate());
        cstmt.close();
    }

    /**
     * Test retrieving multiple resultsets, the return value and an additional
     * output parameter from a single procedure call.
     */
    public void testCallWithResultSet() throws Exception {
        Statement st = con.createStatement();
        st.execute("create proc #testCallWithResultSet @in varchar(16), @out varchar(32) output as" +
                   " begin" +
                   "  select 'result set' as ret" +
                   "  set @out = 'Test ' + @in;" +
                   "  select 'result set 2' as ret2" +
                   "  return 1" +
                   " end");
        st.close();

        CallableStatement cstmt = con.prepareCall("{?=call #testCallWithResultSet(?,?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.setString(2, "data");
        cstmt.registerOutParameter(3, Types.VARCHAR);
        cstmt.execute();

        // resultset 1
        ResultSet rs = cstmt.getResultSet();
        assertNotNull(rs);
        assertTrue(rs.next());
        assertEquals("result set", rs.getString(1));
        assertFalse(rs.next());
        rs.close();

        // resultset 2
        assertTrue(cstmt.getMoreResults());
        rs = cstmt.getResultSet();
        assertTrue(rs.next());
        assertEquals("result set 2", rs.getString(1));
        assertFalse(rs.next());
        rs.close();

        // return value and output parameter
        assertEquals(1, cstmt.getInt(1));
        assertEquals("Test data", cstmt.getString(3));
        cstmt.close();
    }

   /**
    *
    */
   public void testBug637()
      throws Exception
   {
      Statement stm = con.createStatement();
      stm.executeUpdate( "create table #testBug637( a int, b int )" );

      CallableStatement stmt = null;

      try
      {
         // prepareCall() should fail, this is no procedure call
         stmt = con.prepareCall( "INSERT INTO #testBug637( a, b ) VALUES( ?, ? )" );
         stmt.setInt( 1, 1 );
         // this failed prior to SVN revision 1146
         stmt.setInt( 2, 2 );

         fail();
      }
      catch( SQLException sqle )
      {
         assertEquals( "07000", sqle.getSQLState() );
      }
      finally
      {
         if( stmt != null )
         {
            stmt.close();
         }
      }
      stm.close();
   }

    /**
     * Test that output result sets, return values and output parameters are
     * correctly handled for a remote procedure call.
     * To set up this test you will a local and remote server where the remote
     * server allows logins from the local test server.
     * Install the following stored procedure on the remote server:
     *
     * create proc jtds_remote @in varchar(16), @out varchar(32) output as
     * begin
     *   select 'result set'
     *   set @out = 'Test ' + @in;
     *   return 1
     * end
     *
     * Uncomment this test and amend the remoteserver name in the prepareCall
     * statement below to be the actual name of your remote server.
     *
     * The TDS stream for this test will comprise a result set, a dummy return
     * (0x79) value and then the actual return and output parameter (0xAC) records.
     *
     * This call will fail with jtds 1.1 as the dummy return value of 0 in the
     * TDS stream will preempt the capture of the actual value 1. In addition the
     * return value will be assigned to the output parameter and the actual output
     * parameter value will be lost.
     *
     *
    public void testRemoteCallWithResultSet() throws Exception {
        CallableStatement cstmt = con.prepareCall(
                "{?=call remoteserver.database.user.jtds_remote(?,?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.setString(2, "data");
        cstmt.registerOutParameter(3, Types.VARCHAR);
        cstmt.execute();
        ResultSet rs = cstmt.getResultSet();
        assertNotNull(rs);
        assertTrue(rs.next());
        assertEquals("result set", rs.getString(1));
        assertFalse(rs.next());
        rs.close();
        assertEquals(1, cstmt.getInt(1));
        assertEquals("Test data", cstmt.getString(3));
        cstmt.close();
    }
    */

    public static void main(String[] args) {
        junit.textui.TestRunner.run(CallableStatementTest.class);
    }
}
6 мар 20, 14:27    [22094617]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3531
mad_nazgul
unit-тест проверяет не кусок кода, а контракт.
Т.е. что-то подав на вход, мы что-то получим на выходе.
В т.ч. контракт на ошибки/исключения
Т.е. мы считаем, что у нас "черный ящик" и ждем от него определенного поведения.
Нет, это исключительно ваша фантазия, в юнит-тестах никакого черного ящика нет, там просто код. Вы по какой-то причине пытаетесь дискутировать не в той системе координат: юнит-тесты и функциональные тесты - это совершенно разные понятия.

mad_nazgul
Дорого. А вот изменения данного кода будут стоит гораздо дешевле
Ну как это дешевле-то? У вас добавление поля в сущность стоит столько же сколько написать с нуля.

mad_nazgul
Вообще-то вся медицинская история нужна только одному врачу - терапевту, который ставит диагноз.
И еще патологоанатому, чтобы причину смерти установить

Сообщение было отредактировано: 6 мар 20, 14:43
6 мар 20, 14:38    [22094635]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mayton
Member

Откуда: loopback
Сообщений: 45470
istrebitel
mayton

Если такие тесты "плохие" - то приведи пример пожалуйста хороших и правильных тестов.

Желательно с кодом.

http://jtds.sourceforge.net
+
// jTDS JDBC Driver for Microsoft SQL Server and Sybase
// Copyright (C) 2004 The jTDS Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
package net.sourceforge.jtds.jdbc;

import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;

import junit.framework.AssertionFailedError;
//
// MJH - Changes for new jTDS version
// Added registerOutParameter to testCallableStatementParsing2
//
/**
 * @version 1.0
 */
public class CallableStatementTest extends TestBase {

    /** set to false to enable verbose console output */
    private final boolean SILENT = true;

    public CallableStatementTest(String name) {
        super(name);
    }

   /**
    * Test comment processing, bug #634 (and #676).
    */
   public void testCommentProcessing()
      throws SQLException
   {
      Statement st = con.createStatement();
      st.executeUpdate( "create procedure #sp_bug634 @data1 int, @data2 int as select @data1 + @data2" );
      st.close();

      String[] variants = new String[]
      {
         "{?=call #sp_bug634(?, ?)}",

         "/*/ comment '\"?@[*-} /**/*/?=call #sp_bug634(?, ?)",
         "?/*/ comment '\"?@[*-} /**/*/=call #sp_bug634(?, ?)",
         "?/*/ comment '\"?@[*-} /**/*/=call #sp_bug634(?, ?)",
         "?=/*/ comment '\"?@[*-} /**/*/call #sp_bug634(?, ?)",
         "?=call /*/ comment '\"?@[*-} /**/*/#sp_bug634(?, ?)",
         "?=call #sp_bug634/*/ comment '\"?@[*-} /**/*/(?, ?)",
         "?=call #sp_bug634(/*/ comment '\"?@[*-} /**/*/?, ?)",
         "?=call #sp_bug634(?/*/ comment '\"?@[*-} /**/*/, ?)",
         "?=call #sp_bug634(?,/*/ comment '\"?@[*-} /**/*/ ?)",
         "?=call #sp_bug634(?, ?/*/ comment '\"?@[*-} /**/*/)",
         "?=call #sp_bug634(?, ?)/*/ comment '\"?@[*-} /**/*/",
         "?=call #sp_bug634(?, ?)/*/ comment '\"?@[*-} /**/*/",
         "?=call #sp_bug634(?, ?) -- comment '\"?@[*-",
         "?=call -- comment '\"?@[*-}\n #sp_bug634(?, ?)",
         "?=call #sp_bug634(-- comment '\"?@[*-}\n ?, ?)",

         "/*/ comment '\"?@[*-} /**/*/{?=call #sp_bug634(?, ?)}",
         "{/*/ comment '\"?@[*-} /**/*/?=call #sp_bug634(?, ?)}",
         "{?/*/ comment '\"?@[*-} /**/*/=call #sp_bug634(?, ?)}",
         "{?=/*/ comment '\"?@[*-} /**/*/call #sp_bug634(?, ?)}",
         "{?=call /*/ comment '\"?@[*-} /**/*/#sp_bug634(?, ?)}",
         "{?=call #sp_bug634/*/ comment '\"?@[*-} /**/*/(?, ?)}",
         "{?=call #sp_bug634(/*/ comment '\"?@[*-} /**/*/?, ?)}",
         "{?=call #sp_bug634(?/*/ comment '\"?@[*-} /**/*/, ?)}",
         "{?=call #sp_bug634(?,/*/ comment '\"?@[*-} /**/*/ ?)}",
         "{?=call #sp_bug634(?, ?/*/ comment '\"?@[*-} /**/*/)}",
         "{?=call #sp_bug634(?, ?)/*/ comment '\"?@[*-} /**/*/}",
         "{?=call #sp_bug634(?, ?)}/*/ comment '\"?@[*-} /**/*/",
         "{?=call #sp_bug634(?, ?)} -- comment '\"?@[*-}",
         "{?=call -- comment '\"?@[*-}\n #sp_bug634(?, ?)}",
         "{?=call #sp_bug634(-- comment '\"?@[*-}\n ?, ?)}"
      };

      for( int i = 0; i < variants.length;  i ++ )
      {
         CallableStatement cst = null;
         ResultSet         res = null;

         try
         {
            cst = con.prepareCall( variants[i] );
            cst.registerOutParameter( 1, Types.INTEGER );
            cst.setInt( 2, i );
            cst.setInt( 3, i );
            res = cst.executeQuery();

            assertTrue  ( res.next()             );
            assertEquals( 2 * i, res.getInt( 1 ) );
            assertFalse ( res.next()             );
         }
         catch( SQLException e )
         {
            AssertionFailedError error = new AssertionFailedError( "variant \"" + variants[i] + "\" failed: " + e.getMessage() );
            error.initCause( e );
            throw error;
         }
         finally
         {
            if( res != null ) res.close();
            if( cst != null ) cst.close();
         }
      }
   }

    public void testCallableStatement() throws Exception {
        CallableStatement cstmt = con.prepareCall("{call sp_who}");

        cstmt.close();
    }

    public void testCallableStatement1() throws Exception {
        CallableStatement cstmt = con.prepareCall("sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump(rs,SILENT);

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementCall1() throws Exception {
        CallableStatement cstmt = con.prepareCall("{call sp_who}");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementCall2() throws Exception {
        CallableStatement cstmt = con.prepareCall("{CALL sp_who}");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementCall3() throws Exception {
        CallableStatement cstmt = con.prepareCall("{cAlL sp_who}");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    /**
     * Test for bug [974801] stored procedure error in Northwind
     */
    public void testCallableStatementCall4() throws Exception {
        Statement stmt;

        try {
            stmt = con.createStatement();
            stmt.execute("create procedure \"#test space\" as SELECT COUNT(*) FROM sysobjects");
            stmt.close();

            CallableStatement cstmt = con.prepareCall("{call \"#test space\"}");

            ResultSet rs = cstmt.executeQuery();
            dump( rs,SILENT );

            rs.close();
            cstmt.close();
        } finally {
            stmt = con.createStatement();
            stmt.execute("drop procedure \"#test space\"");
            stmt.close();
        }
    }

    public void testCallableStatementExec1() throws Exception {
        CallableStatement cstmt = con.prepareCall("exec sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec2() throws Exception {
        CallableStatement cstmt = con.prepareCall("EXEC sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec3() throws Exception {
        CallableStatement cstmt = con.prepareCall("execute sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec4() throws Exception {
        CallableStatement cstmt = con.prepareCall("EXECUTE sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec5() throws Exception {
        CallableStatement cstmt = con.prepareCall("eXeC sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec6() throws Exception {
        CallableStatement cstmt = con.prepareCall("ExEcUtE sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec7() throws Exception {
        CallableStatement cstmt = con.prepareCall("execute \"master\"..sp_who");

        ResultSet rs = cstmt.executeQuery();
        dump( rs,SILENT );

        rs.close();
        cstmt.close();
    }

    public void testCallableStatementExec8() throws Exception {
        Statement stmt;

        try {
            stmt = con.createStatement();
            stmt.execute("create procedure #test as SELECT COUNT(*) FROM sysobjects");
            stmt.close();

            CallableStatement cstmt = con.prepareCall("execute #test");

            ResultSet rs = cstmt.executeQuery();
            dump( rs,SILENT );

            rs.close();
            cstmt.close();
        } finally {
            stmt = con.createStatement();
            stmt.execute("drop procedure #test");
            stmt.close();
        }
    }

    /**
     * Test for bug [978175] 0.8: Stored Procedure call doesn't work anymore
     */
    public void testCallableStatementExec9() throws Exception {
        CallableStatement cstmt = con.prepareCall("{call sp_who}");

        assertTrue(cstmt.execute());

        ResultSet rs = cstmt.getResultSet();

        if (rs == null) {
            fail("Null ResultSet returned");
        } else {
            dump( rs,SILENT );
            rs.close();
        }

        cstmt.close();
    }

    public void testCallableStatementParsing1() throws Exception {
        String data = "New {order} plus {1} more";
        Statement stmt = con.createStatement();

        stmt.execute("CREATE TABLE #csp1 (data VARCHAR(32))");
        stmt.close();

        stmt = con.createStatement();
        stmt.execute("create procedure #sp_csp1 @data VARCHAR(32) as INSERT INTO #csp1 (data) VALUES(@data)");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #sp_csp1(?)}");

        cstmt.setString(1, data);
        cstmt.execute();
        cstmt.close();

        stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT data FROM #csp1");

        assertTrue(rs.next());

        assertTrue(data.equals(rs.getString(1)));

        assertTrue(!rs.next());
        rs.close();
        stmt.close();
    }

    /**
     * Test for bug [938632] String index out of bounds error in 0.8rc1.
     */
    public void testCallableStatementParsing2() throws Exception {
        try {
            Statement stmt = con.createStatement();

            stmt.execute("create procedure #load_smtp_in_1gr_ls804192 as SELECT name FROM sysobjects");
            stmt.close();

            CallableStatement cstmt = con.prepareCall("{?=call #load_smtp_in_1gr_ls804192}");
            cstmt.registerOutParameter(1, java.sql.Types.INTEGER); // MJH 01/05/04
            cstmt.execute();
            cstmt.close();
        } finally {
            Statement stmt = con.createStatement();

            stmt.execute("drop procedure #load_smtp_in_1gr_ls804192");
            stmt.close();
        }
    }

    /**
     * Test for bug [1006845] Stored procedure with 18 parameters.
     */
    public void testCallableStatementParsing3() throws Exception {
        CallableStatement cstmt = con.prepareCall("{Call Test(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}");
        cstmt.close();
    }

    /**
     * Test for incorrect exception thrown/no exception thrown when invalid
     * call escape is used.
     * <p/>
     * See https://sourceforge.net/forum/forum.php?thread_id=1144619&forum_id=104389
     * for more detail.
     */
    public void testCallableStatementParsing4() throws SQLException {
        try {
            con.prepareCall("{call ? = sp_create_employee (?, ?, ?, ?, ?, ?)}");
            fail("Was expecting an invalid escape sequence error");
        } catch (SQLException ex) {
            assertEquals("22025", ex.getSQLState());
        }
    }

    /**
     * Test for bug [1052942] Error processing JDBC call escape. (A blank
     * before the final <code>}</code> causes the parser to fail).
     */
    public void testCallableStatementParsing5() throws Exception {
        CallableStatement cstmt = con.prepareCall(" { Call Test(?,?) } ");
        cstmt.close();
    }

    /**
     * Test for incorrect exception thrown/no exception thrown when invalid
     * call escape is used.
     * <p/>
     * A message containing the correct missing terminator should be generated.
     */
    public void testCallableStatementParsing6() throws SQLException {
        try {
            con.prepareCall("{call sp_test(?, ?)");
            fail("Was expecting an invalid escape error");
        } catch (SQLException ex) {
            assertEquals("22025", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf('}') != -1);
        }
    }

    /**
     * Test for incorrect exception thrown/no exception thrown when invalid
     * call escape is used.
     * <p/>
     * A message containing the correct missing terminator should be generated.
     */
    public void testCallableStatementParsing7() throws SQLException {
        try {
            con.prepareCall("{call sp_test(?, ?}");
            fail("Was expecting an invalid escape error");
        } catch (SQLException ex) {
            assertEquals("22025", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf(')') != -1);
        }
    }

    /**
     * Test for reature request [956800] setNull(): Not implemented.
     */
    public void testCallableSetNull1() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("CREATE TABLE #callablesetnull1 (data CHAR(1) NULL)");
        stmt.close();

        try {
            stmt = con.createStatement();
            stmt.execute("create procedure #procCallableSetNull1 @data char(1) "
                     + "as INSERT INTO #callablesetnull1 (data) VALUES (@data)");
            stmt.close();

            CallableStatement cstmt = con.prepareCall("{call #procCallableSetNull1(?)}");
            // Test CallableStatement.setNull(int,Types.NULL)
            cstmt.setNull(1, Types.NULL);
            cstmt.execute();
            cstmt.close();

            stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT data FROM #callablesetnull1");

            assertTrue(rs.next());

            // Test ResultSet.getString()
            assertNull(rs.getString(1));
            assertTrue(rs.wasNull());

            assertTrue(!rs.next());
            stmt.close();
            rs.close();
        } finally {
            stmt = con.createStatement();
            stmt.execute("drop procedure #procCallableSetNull1");
            stmt.close();
        }
    }

    /**
     * Test for bug [974284] retval on callable statement isn't handled correctly
     */
    public void testCallableRegisterOutParameter1() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("create procedure #rop1 @a varchar(1), @b varchar(1) as\r\n "
                     + "begin\r\n"
                     + "return 1\r\n"
                     + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{? = call #rop1(?, ?)}");

        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.setString(2, "a");
        cstmt.setString(3, "b");
        cstmt.execute();

        assertEquals(1, cstmt.getInt(1));
        assertEquals("1", cstmt.getString(1));

        cstmt.close();
    }

    /**
     * Test for bug [994888] Callable statement and Float output parameter
     */
    public void testCallableRegisterOutParameter2() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("create procedure #rop2 @data float OUTPUT as\r\n "
                     + "begin\r\n"
                     + "set @data = 1.1\r\n"
                     + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #rop2(?)}");

        cstmt.registerOutParameter(1, Types.FLOAT);
        cstmt.execute();

        assertTrue(cstmt.getFloat(1) == 1.1f);
        cstmt.close();
    }

    /**
     * Test for bug [994988] Network error when null is returned via int output parm
     */
    public void testCallableRegisterOutParameter3() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("create procedure #rop3 @data int OUTPUT as\r\n "
                     + "begin\r\n"
                     + "set @data = null\r\n"
                     + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #rop3(?)}");

        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.execute();

        cstmt.getInt(1);
        assertTrue(cstmt.wasNull());
        cstmt.close();
    }

   /**
    * Test for bug [983432] Prepared call doesn't work with jTDS 0.8
    */
   public void testCallableRegisterOutParameter4()
      throws Exception
   {
      // cleanup remains from last run
      dropProcedure( "rop4" );
      dropType( "T_INTEGER" );

      CallableStatement cstmt = con.prepareCall( "{call sp_addtype T_INTEGER, int, 'NULL'}" );
      Statement stmt = con.createStatement();

      try
      {
         cstmt.execute();
         cstmt.close();

         stmt.execute( "create procedure rop4 @data T_INTEGER OUTPUT as\r\n " + "begin\r\n" + "set @data = 1\r\n" + "end" );
         stmt.close();

         cstmt = con.prepareCall( "{call rop4(?)}" );

         cstmt.registerOutParameter( 1, Types.VARCHAR );
         cstmt.execute();

         assertEquals( cstmt.getInt( 1 ), 1 );
         assertTrue( !cstmt.wasNull() );
         cstmt.close();

         cstmt = con.prepareCall( "rop4 ?" );

         cstmt.registerOutParameter( 1, Types.VARCHAR );
         cstmt.execute();

         assertEquals( cstmt.getInt( 1 ), 1 );
         assertTrue( !cstmt.wasNull() );
         cstmt.close();
      }
      finally
      {
         // cleanup
         dropProcedure( "rop4" );
         dropType( "T_INTEGER" );
      }
   }

   /**
    * Test for bug [946171] null boolean in CallableStatement bug
    */
   public void testCallableRegisterOutParameter5() throws Exception {
       Statement stmt = con.createStatement();
       stmt.execute("create procedure #rop1 @bool bit, @whatever int OUTPUT as\r\n "
                    + "begin\r\n"
                    + "set @whatever = 1\r\n"
                    + "end");
       stmt.close();

       try {
           CallableStatement cstmt = con.prepareCall("{call #rop1(?,?)}");

           cstmt.setNull(1, Types.BOOLEAN);
           cstmt.registerOutParameter(2, Types.INTEGER);
           cstmt.execute();

           assertTrue(cstmt.getInt(2) == 1);
           cstmt.close();
       } finally {
           stmt = con.createStatement();
           stmt.execute("drop procedure #rop1");
           stmt.close();
       }
   }

   /**
    * Test for bug [992715] wasnull() always returns false
    */
   public void testCallableRegisterOutParameter6() throws Exception {
       Statement stmt = con.createStatement();
       stmt.execute("create procedure #rop2 @bool bit, @whatever varchar(1) OUTPUT as\r\n "
                    + "begin\r\n"
                    + "set @whatever = null\r\n"
                    + "end");
       stmt.close();

       CallableStatement cstmt = con.prepareCall("{call #rop2(?,?)}");

       cstmt.setNull(1, Types.BOOLEAN);
       cstmt.registerOutParameter(2, Types.VARCHAR);
       cstmt.execute();

       assertTrue(cstmt.getString(2) == null);
       assertTrue(cstmt.wasNull());
       cstmt.close();
   }

    /**
     * Test for bug [991640] java.sql.Date error and RAISERROR problem
     */
    public void testCallableError1() throws Exception {
        String text = "test message";

        Statement stmt = con.createStatement();
        stmt.execute("create procedure #ce1 as\r\n "
                     + "begin\r\n"
                     + "RAISERROR('" + text + "', 16, 1 )\r\n"
                     + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #ce1}");

        try {
            cstmt.execute();
            assertTrue(false);
        } catch (SQLException e) {
            assertTrue(e.getMessage().equals(text));
        }

        cstmt.close();
    }

   /**
    * Test named parameters.
    */
   public void testNamedParameters0001()
      throws Exception
   {
      final String data = "New {order} plus {1} more";
      final String outData = "test";

      Statement stmt = con.createStatement();

      stmt.execute( "CREATE TABLE #csn1 ( data VARCHAR(32) )" );
      stmt.execute( "create procedure #sp_csn1 @data VARCHAR(32) OUT as INSERT INTO #csn1 (data) VALUES(@data) SET @data = '" + outData + "'" + "RETURN 13" );

      CallableStatement cstmt = con.prepareCall( "{?=call #sp_csn1(?)}" );

      cstmt.registerOutParameter( "@return_status", Types.INTEGER );
      cstmt.setString( "@data", data );
      cstmt.registerOutParameter( "@data", Types.VARCHAR );
      assertEquals( 1, cstmt.executeUpdate() );
      assertFalse( cstmt.getMoreResults() );
      assertEquals( -1, cstmt.getUpdateCount() );
      assertEquals( outData, cstmt.getString( "@data" ) );
      cstmt.close();

      ResultSet rs = stmt.executeQuery( "SELECT data FROM #csn1" );

      assertTrue( rs.next() );
      assertEquals( data, rs.getString( 1 ) );
      assertTrue( ! rs.next() );

      rs.close();
      stmt.close();
   }

   /**
    * Test named parameters.
    */
   public void testNamedParameters0002()
      throws Exception
   {
      final String  A_DEFAULT = "XYZ";
      final Integer B_DEFAULT = 123;
      final Integer C_DEFAULT = 321;

      Statement stmt = con.createStatement();
      stmt.execute( "create table #Test ( A varchar(10), B int, C int, D int primary key )" );
      stmt.execute( "create procedure #spInsert @A_VAL varchar(10) = " + A_DEFAULT + " out, @B_VAL int = " + B_DEFAULT + ", @C_VAL int = " + C_DEFAULT + " out, @D_VAL int as INSERT INTO #Test VALUES( @A_VAL, @B_VAL, @C_VAL, @D_VAL ) set @A_VAL = 'RET' set @C_VAL = @B_VAL + @C_VAL return @B_VAL" );

      CallableStatement cstmt = con.prepareCall( "{?=call #spInsert(?, ?, ?, ?)}" );

      cstmt.registerOutParameter( 1, Types.INTEGER );
      cstmt.registerOutParameter( "A_VAL", Types.VARCHAR );
      cstmt.registerOutParameter( "C_VAL", Types.INTEGER );

      cstmt.setObject( "A_VAL", A_DEFAULT );
      cstmt.setObject( "B_VAL", B_DEFAULT );
      cstmt.setObject( "C_VAL", C_DEFAULT );
      cstmt.setInt   ( "D_VAL", 0         );

      assertEquals( 1, cstmt.executeUpdate() );
      assertFalse( cstmt.getMoreResults() );
      assertEquals( -1, cstmt.getUpdateCount() );

      assertEquals( B_DEFAULT, cstmt.getObject( 1 ) );
      assertEquals( "RET", cstmt.getObject( "A_VAL" ) );
      assertEquals( B_DEFAULT + C_DEFAULT, cstmt.getObject( "C_VAL" ) );
      cstmt.close();

      ResultSet rs = stmt.executeQuery( "select A, B, C from #Test where D = 0" );

      assertTrue( rs.next() );
      assertEquals( A_DEFAULT, rs.getObject( "A" ) );
      assertEquals( B_DEFAULT, rs.getObject( "B" ) );
      assertEquals( C_DEFAULT, rs.getObject( "C" ) );
      assertTrue( ! rs.next() );

      rs.close();

      // and once again without setting all parameters

      cstmt = con.prepareCall( "{?=call #spInsert(?,?)}" );

      cstmt.registerOutParameter( 1, Types.INTEGER );

      cstmt.setInt( "B_VAL", 9876 );
      cstmt.setInt( "D_VAL", 1    );

      assertEquals( 1, cstmt.executeUpdate() );
      assertFalse( cstmt.getMoreResults() );
      assertEquals( -1, cstmt.getUpdateCount() );

      assertEquals( 9876, cstmt.getObject( 1 ) );
      cstmt.close();

      rs = stmt.executeQuery( "select A, B, C from #Test where D = 1" );

      assertTrue( rs.next() );
      assertEquals( A_DEFAULT, rs.getObject( "A" ) );
      assertEquals( 9876     , rs.getObject( "B" ) );
      assertEquals( C_DEFAULT, rs.getObject( "C" ) );
      assertTrue( ! rs.next() );

      rs.close();
      stmt.close();
   }

   /**
     * Test that procedure outputs are available immediately for procedures
     * that do not return ResultSets (i.e that update counts are cached).
     */
    public void testProcessUpdateCounts1() throws SQLException {
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testProcessUpdateCounts1 (val INT)"));
        assertFalse(stmt.execute("CREATE PROCEDURE #procTestProcessUpdateCounts1"
                + " @res INT OUT AS"
                + " INSERT INTO #testProcessUpdateCounts1 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts1 SET val = 2"
                + " INSERT INTO #testProcessUpdateCounts1 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts1 SET val = 3"
                + " SET @res = 13"
                + " RETURN 14"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall(
                "{?=call #procTestProcessUpdateCounts1(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.registerOutParameter(2, Types.INTEGER);

        assertFalse(cstmt.execute());
        assertEquals(14, cstmt.getInt(1));
        assertEquals(13, cstmt.getInt(2));

        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(2, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(-1, cstmt.getUpdateCount());

        cstmt.close();
    }

    /**
     * Test that procedure outputs are available immediately after processing
     * the last ResultSet returned by the procedure (i.e that update counts
     * are cached).
     */
    public void testProcessUpdateCounts2() throws SQLException {
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testProcessUpdateCounts2 (val INT)"));
        assertFalse(stmt.execute("CREATE PROCEDURE #procTestProcessUpdateCounts2"
                + " @res INT OUT AS"
                + " INSERT INTO #testProcessUpdateCounts2 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts2 SET val = 2"
                + " SELECT * FROM #testProcessUpdateCounts2"
                + " INSERT INTO #testProcessUpdateCounts2 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts2 SET val = 3"
                + " SET @res = 13"
                + " RETURN 14"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall(
                "{?=call #procTestProcessUpdateCounts2(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.registerOutParameter(2, Types.INTEGER);

        assertFalse(cstmt.execute());
        try {
            assertEquals(14, cstmt.getInt(1));
            assertEquals(13, cstmt.getInt(2));
            // Don't fail the test if we got here. Another driver or a future
            // version could cache all the results and obtain the output
            // parameter values from the beginning.
        } catch (SQLException ex) {
            assertEquals("HY010", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf("getMoreResults()") >= 0);
        }

        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // UPDATE

        assertTrue(cstmt.getMoreResults()); // SELECT

        assertFalse(cstmt.getMoreResults());
        assertEquals(14, cstmt.getInt(1));
        assertEquals(13, cstmt.getInt(2));
        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(2, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(-1, cstmt.getUpdateCount());

        cstmt.close();
    }

    /**
     * Test that procedure outputs are available immediately after processing
     * the last ResultSet returned by the procedure (i.e that update counts
     * are cached) even if getMoreResults() is not called.
     */
    public void testProcessUpdateCounts3() throws SQLException {
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testProcessUpdateCounts3 (val INT)"));
        assertFalse(stmt.execute("CREATE PROCEDURE #procTestProcessUpdateCounts3"
                + " @res INT OUT AS"
                + " INSERT INTO #testProcessUpdateCounts3 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts3 SET val = 2"
                + " SELECT * FROM #testProcessUpdateCounts3"
                + " INSERT INTO #testProcessUpdateCounts3 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts3 SET val = 3"
                + " SET @res = 13"
                + " RETURN 14"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall(
                "{?=call #procTestProcessUpdateCounts3(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.registerOutParameter(2, Types.INTEGER);

        assertFalse(cstmt.execute());
        try {
            assertEquals(14, cstmt.getInt(1));
            assertEquals(13, cstmt.getInt(2));
            // Don't fail the test if we got here. Another driver or a future
            // version could cache all the results and obtain the output
            // parameter values from the beginning.
        } catch (SQLException ex) {
            assertEquals("HY010", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf("getMoreResults()") >= 0);
        }

        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // UPDATE

        assertTrue(cstmt.getMoreResults()); // SELECT
        ResultSet rs = cstmt.getResultSet();
        assertNotNull(rs);
        // Close the ResultSet; this should cache the following update counts
        rs.close();

        assertEquals(14, cstmt.getInt(1));
        assertEquals(13, cstmt.getInt(2));

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(2, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(-1, cstmt.getUpdateCount());

        cstmt.close();
    }

    /**
     * Test that procedure outputs are available immediately after processing
     * the last ResultSet returned by the procedure (i.e that update counts
     * are cached) even if getMoreResults() and ResultSet.close() are not
     * called.
     */
    public void testProcessUpdateCounts4() throws SQLException {
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testProcessUpdateCounts4 (val INT)"));
        assertFalse(stmt.execute("CREATE PROCEDURE #procTestProcessUpdateCounts4"
                + " @res INT OUT AS"
                + " INSERT INTO #testProcessUpdateCounts4 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts4 SET val = 2"
                + " SELECT * FROM #testProcessUpdateCounts4"
                + " INSERT INTO #testProcessUpdateCounts4 VALUES (1)"
                + " UPDATE #testProcessUpdateCounts4 SET val = 3"
                + " SET @res = 13"
                + " RETURN 14"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall(
                "{?=call #procTestProcessUpdateCounts4(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.registerOutParameter(2, Types.INTEGER);

        assertFalse(cstmt.execute());
        try {
            assertEquals(14, cstmt.getInt(1));
            assertEquals(13, cstmt.getInt(2));
            // Don't fail the test if we got here. Another driver or a future
            // version could cache all the results and obtain the output
            // parameter values from the beginning.
        } catch (SQLException ex) {
            assertEquals("HY010", ex.getSQLState());
            assertTrue(ex.getMessage().indexOf("getMoreResults()") >= 0);
        }

        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // UPDATE

        assertTrue(cstmt.getMoreResults()); // SELECT
        ResultSet rs = cstmt.getResultSet();
        assertNotNull(rs);
        // Process all rows; this should cache the following update counts
        assertTrue(rs.next());
        assertFalse(rs.next());

        assertEquals(14, cstmt.getInt(1));
        assertEquals(13, cstmt.getInt(2));

        // Only close the ResultSet now
        rs.close();

        assertFalse(cstmt.getMoreResults());
        assertEquals(1, cstmt.getUpdateCount()); // INSERT

        assertFalse(cstmt.getMoreResults());
        assertEquals(2, cstmt.getUpdateCount()); // UPDATE

        assertFalse(cstmt.getMoreResults());
        assertEquals(-1, cstmt.getUpdateCount());

        cstmt.close();
    }

    /**
     * Test for bug [ 1062671 ] SQLParser unable to parse CONVERT(char,{ts ?},102)
     */
    public void testTsEscape() throws Exception {
        Timestamp ts = Timestamp.valueOf("2004-01-01 23:56:56");
        Statement stmt = con.createStatement();
        assertFalse(stmt.execute("CREATE TABLE #testTsEscape (val DATETIME)"));
        PreparedStatement pstmt = con.prepareStatement("INSERT INTO #testTsEscape VALUES({ts ?})");
        pstmt.setTimestamp(1, ts);
        assertEquals(1, pstmt.executeUpdate());
        ResultSet rs = stmt.executeQuery("SELECT * FROM #testTsEscape");
        assertTrue(rs.next());
        assertEquals(ts, rs.getTimestamp(1));
    }

    /**
     * Test for separation of IN and INOUT/OUT parameter values
     */
    public void testInOutParameters() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("CREATE PROC #testInOut @in int, @out int output as SELECT @out = @out + @in");
        CallableStatement cstmt = con.prepareCall("{ call #testInOut ( ?,? ) }");
        cstmt.setInt(1, 1);
        cstmt.registerOutParameter(2, Types.INTEGER);
        cstmt.setInt(2, 2);
        cstmt.execute();
        assertEquals(3, cstmt.getInt(2));
        cstmt.execute();
        assertEquals(3, cstmt.getInt(2));
    }

    /**
     * Test that procedure names containing semicolons are parsed correctly.
     */
    public void testSemicolonProcedures() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("CREATE PROC #testInOut @in int, @out int output as SELECT @out = @out + @in");
        CallableStatement cstmt = con.prepareCall("{call #testInOut;1(?,?)}");
        cstmt.setInt(1, 1);
        cstmt.registerOutParameter(2, Types.INTEGER);
        cstmt.setInt(2, 2);
        cstmt.execute();
        assertEquals(3, cstmt.getInt(2));
        cstmt.execute();
        assertEquals(3, cstmt.getInt(2));
    }

    /**
     * Test that procedure calls with both literal parameters and parameterr
     * markers are executed correctly (bug [1078927] Callable statement fails).
     */
    public void testNonRpcProc1() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute(
                "create proc #testsp1 @p1 int, @p2 int out as set @p2 = @p1");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #testsp1(100, ?)}");
        cstmt.setInt(1, 1);
        cstmt.execute();
        cstmt.close();
    }

    /**
     * Test that procedure calls with both literal parameters and parameterr
     * markers are executed correctly (bug [1078927] Callable statement fails).
     */
    public void testNonRpcProc2() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("create proc #testsp2 @p1 int, @p2 int as return 99");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{?=call #testsp2(100, ?)}");
        cstmt.registerOutParameter(1, java.sql.Types.INTEGER);
        cstmt.setInt(2, 2);
        cstmt.execute();
        assertEquals(99, cstmt.getInt(1));
        cstmt.close();
    }

    /**
     * Test for bug [1152329] Spurious output params assigned (TIMESTMP).
     * <p/>
     * If a stored procedure execute WRITETEXT or UPDATETEXT commands, spurious
     * output parameter data is returned to the client. This additional data
     * can be confused with the real output parameter data leading to an output
     * string parameter returning the text ?TIMESTMP? on SQL Server 7+ or
     * binary garbage on other servers.
     */
    public void testWritetext() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute(
                "create proc #testWritetext @p1 varchar(20) output as "
                + "begin "
                + "create table #test (id int, txt text) "
                + "insert into #test (id, txt) values(1, '') "
                + "declare @ptr binary(16) "
                + "select @ptr = (select textptr(txt) from #test where id = 1) "
                + "writetext #test.txt @ptr 'This is a test' "
                + "select @p1 = 'done' "
                + "end");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #testWritetext(?)}");
        cstmt.registerOutParameter(1, Types.VARCHAR);
        cstmt.execute();
        assertEquals("done", cstmt.getString(1));
        cstmt.close();
    }

    /**
     * Test for bug [1047208] SQLException chaining not implemented correctly:
     * checks that all errors are returned and that output variables are also
     * returned.
     */
    public void testErrorOutputParams() throws Exception {
        Statement stmt = con.createStatement();
        stmt.execute("CREATE PROC #error_proc @p1 int out AS \r\n" +
                     "RAISERROR ('TEST EXCEPTION', 15, 1)\r\n" +
                     "SELECT @P1=100\r\n" +
                     "CREATE TABLE #DUMMY (id int)\r\n" +
                     "INSERT INTO #DUMMY VALUES(1)\r\n"+
                     "INSERT INTO #DUMMY VALUES(1)");
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #error_proc(?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        try {
            cstmt.execute();
            fail("Expecting exception");
        } catch (SQLException e) {
            assertEquals("TEST EXCEPTION", e.getMessage());
        }
        assertEquals(100, cstmt.getInt(1));
        cstmt.close();
    }

    /**
     * Test for bug [1236078] Procedure doesn't get called for some BigDecimal
     * values - invalid bug.
     */
    public void testBigDecimal() throws Exception {
        Statement stmt = con.createStatement();
        assertEquals(0, stmt.executeUpdate("CREATE TABLE #dec_test "
                + "(ColumnVC varchar(50) NULL, ColumnDec decimal(18,4) NULL)"));
        assertEquals(0, stmt.executeUpdate("CREATE PROCEDURE #dec_test2"
                + "(@inVc varchar(32), @inBd decimal(18,4)) AS "
                + "begin "
                + "update #dec_test set columnvc = @inVc, columndec = @inBd "
                + "end"));
        assertEquals(1, stmt.executeUpdate(
                "insert #dec_test (columnvc, columndec) values (null, null)"));
        stmt.close();

        CallableStatement cstmt = con.prepareCall("{call #dec_test2 (?,?)}");
        cstmt.setString(1, "D: " + new java.util.Date());
        cstmt.setBigDecimal(2, new BigDecimal("2.9E+7"));
        assertEquals(1, cstmt.executeUpdate());
        cstmt.close();
    }

    /**
     * Test retrieving multiple resultsets, the return value and an additional
     * output parameter from a single procedure call.
     */
    public void testCallWithResultSet() throws Exception {
        Statement st = con.createStatement();
        st.execute("create proc #testCallWithResultSet @in varchar(16), @out varchar(32) output as" +
                   " begin" +
                   "  select 'result set' as ret" +
                   "  set @out = 'Test ' + @in;" +
                   "  select 'result set 2' as ret2" +
                   "  return 1" +
                   " end");
        st.close();

        CallableStatement cstmt = con.prepareCall("{?=call #testCallWithResultSet(?,?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.setString(2, "data");
        cstmt.registerOutParameter(3, Types.VARCHAR);
        cstmt.execute();

        // resultset 1
        ResultSet rs = cstmt.getResultSet();
        assertNotNull(rs);
        assertTrue(rs.next());
        assertEquals("result set", rs.getString(1));
        assertFalse(rs.next());
        rs.close();

        // resultset 2
        assertTrue(cstmt.getMoreResults());
        rs = cstmt.getResultSet();
        assertTrue(rs.next());
        assertEquals("result set 2", rs.getString(1));
        assertFalse(rs.next());
        rs.close();

        // return value and output parameter
        assertEquals(1, cstmt.getInt(1));
        assertEquals("Test data", cstmt.getString(3));
        cstmt.close();
    }

   /**
    *
    */
   public void testBug637()
      throws Exception
   {
      Statement stm = con.createStatement();
      stm.executeUpdate( "create table #testBug637( a int, b int )" );

      CallableStatement stmt = null;

      try
      {
         // prepareCall() should fail, this is no procedure call
         stmt = con.prepareCall( "INSERT INTO #testBug637( a, b ) VALUES( ?, ? )" );
         stmt.setInt( 1, 1 );
         // this failed prior to SVN revision 1146
         stmt.setInt( 2, 2 );

         fail();
      }
      catch( SQLException sqle )
      {
         assertEquals( "07000", sqle.getSQLState() );
      }
      finally
      {
         if( stmt != null )
         {
            stmt.close();
         }
      }
      stm.close();
   }

    /**
     * Test that output result sets, return values and output parameters are
     * correctly handled for a remote procedure call.
     * To set up this test you will a local and remote server where the remote
     * server allows logins from the local test server.
     * Install the following stored procedure on the remote server:
     *
     * create proc jtds_remote @in varchar(16), @out varchar(32) output as
     * begin
     *   select 'result set'
     *   set @out = 'Test ' + @in;
     *   return 1
     * end
     *
     * Uncomment this test and amend the remoteserver name in the prepareCall
     * statement below to be the actual name of your remote server.
     *
     * The TDS stream for this test will comprise a result set, a dummy return
     * (0x79) value and then the actual return and output parameter (0xAC) records.
     *
     * This call will fail with jtds 1.1 as the dummy return value of 0 in the
     * TDS stream will preempt the capture of the actual value 1. In addition the
     * return value will be assigned to the output parameter and the actual output
     * parameter value will be lost.
     *
     *
    public void testRemoteCallWithResultSet() throws Exception {
        CallableStatement cstmt = con.prepareCall(
                "{?=call remoteserver.database.user.jtds_remote(?,?)}");
        cstmt.registerOutParameter(1, Types.INTEGER);
        cstmt.setString(2, "data");
        cstmt.registerOutParameter(3, Types.VARCHAR);
        cstmt.execute();
        ResultSet rs = cstmt.getResultSet();
        assertNotNull(rs);
        assertTrue(rs.next());
        assertEquals("result set", rs.getString(1));
        assertFalse(rs.next());
        rs.close();
        assertEquals(1, cstmt.getInt(1));
        assertEquals("Test data", cstmt.getString(3));
        cstmt.close();
    }
    */

    public static void main(String[] args) {
        junit.textui.TestRunner.run(CallableStatementTest.class);
    }
}

Вы это разрабатываете?
6 мар 20, 14:40    [22094639]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3531
istrebitel
mayton

Если такие тесты "плохие" - то приведи пример пожалуйста хороших и правильных тестов.

Желательно с кодом.

http://jtds.sourceforge.net
Это интеграционные, тут ни у кого не возникает сомнений в их полезности и малых трудозатратах на поддержку при уже существующей инфраструктуре.

Сообщение было отредактировано: 6 мар 20, 14:44
6 мар 20, 14:41    [22094641]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
mayton
Member

Откуда: loopback
Сообщений: 45470
mad_nazgul

Принцип "Лучше день потерять, потом за пят минут долететь"

Я не согласен с этим принципом. Заказчик вообще никогда не заказывает модульные тесты
как часть поставки. Здесь не может быть метрик из серии "мы недельку посидим попишем тесты"
зато потом будем - в шоколаде. Вообще время потраченное на тесты не линейно покрывает
аспекты отсуствия ошибок.
6 мар 20, 14:53    [22094651]     Ответить | Цитировать Сообщить модератору
 Re: unit-тестирование  [new]
istrebitel
Member

Откуда:
Сообщений: 49
mayton

Вы это разрабатываете?

Нет, я пользователь этого.
10 мар 20, 05:26    [22095827]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: Ctrl  назад   1 .. 13 14 15 16 17 18 19 [20] 21 22   вперед  Ctrl
Все форумы / Java Ответить