Есть ли способ проверить другие утверждения внутри теста после вызова tSQLt.ExpectException?

Используя tSQLt 1.0.5873.27393, я пытаюсь написать тест tSQLt для хранимой процедуры, которая записывает ошибки, попавшие в блок CATCH, в таблицу журнала, прежде чем повторно выдать ошибку в вызывающий сеанс.

Я могу успешно проверить, что сообщение об ошибке выдается повторно, используя tSQLt.ExpectException, но побочный эффект, по-видимому, заключается в том, что невозможно проверить какие-либо другие утверждения после вызова tSQLt.ExpectException, поэтому я не могу проверить, была ли ошибка записана в журнал стол.

Самый простой тест, который я могу придумать, который демонстрирует проблему:

CREATE PROC MyTests.[test ExpectException]
AS
BEGIN
    EXEC tSQLt.ExpectException;
    THROW 50001, 'Error message',1

    EXEC tSQLt.AssertEquals 0,1, '0=1'
END
GO

который производит следующий (неожиданный) вывод при выполнении:

|No|Test Case Name                  |Dur(ms)|Result |
+--+--------------------------------+-------+-------+
|1 |[MyTests].[test ExpectException]|      3|Success|

Используя трассировку профилировщика, я вижу, что утверждение AssertEquals никогда не выполняется, потому что ошибка перехватывается блоком CATCH внутри tSQLt.Private_RunTest.

Поскольку tSQLt использует CATCH для перехвата ошибок, я подозреваю, что нет решения этой проблемы без серьезной перезаписи tSQLt, но я спрашиваю здесь на случай, если другие нашли способ обойти проблему.


person Ed Harper    schedule 09.08.2016    source источник
comment
Предложение/лучшая практика: сделайте ваши тесты как можно более атомарными (тестируйте одну вещь за раз). Я бы разделил этот сценарий на два теста (один для ошибки и один для ведения журнала).   -  person Eduard Uta    schedule 09.08.2016
comment
@EduardUta - я бы сделал это, если бы мог, но в данном случае это невозможно; путь кода, который я тестирую, выполняется только при возникновении ошибки, и ошибка всегда впоследствии возникает повторно.   -  person Ed Harper    schedule 09.08.2016
comment
Да, ты можешь. Один тест использует ExpectException, а другой просматривает только таблицу журнала с использованием подхода TRY...CATCH.   -  person Sebastian Meine    schedule 09.08.2016
comment
@SebastianMeine - спасибо за разъяснение   -  person Ed Harper    schedule 09.08.2016


Ответы (3)


Вы можете следовать этому подходу:

В тесте поместите вызов тестируемой хранимой процедуры в блок try/catch. Перед блоком try/catch установите для ожидаемой переменной значение 1, а для фактического — 0. В тестовом блоке catch проверьте, заполнена ли таблица журнала, и если да, то измените фактическую переменную на 1. Напишите свое утверждение ПОСЛЕ блока catch. .

CREATE PROC MyTests.[test ExpectException]
AS
    BEGIN
    DECLARE @expected bit = 1;
    DECLARE @actual bit = 0;

    BEGIN TRY
        -- call tested SP, make sure it fails
        CALL SP..;

        -- add safety net, if the SP call doesn't fail then fail the test
        RAISERROR('Fail the test', 16, 1);
    END TRY
    BEGIN CATCH
        -- pseudo code
        IF EXISTS (SELECT 1 FROM log table)
        -> flip @actual to 1
    END CATCH

    EXEC tSQLt.AssertEquals @expected, @actual
END
GO
person Eduard Uta    schedule 09.08.2016

@ Эдуард Ута добрался до меня раньше, но я все равно опубликую это.


Почти сразу после того, как я опубликовал это, меня осенило, что очевидным решением является использование пользовательского блока TRY...CATCH, а не встроенных объектов tSQLt:

CREATE PROC MyTests.[test custom try catch]
AS
BEGIN
    DECLARE @ErrorRaised bit = 0

    BEGIN TRY
        THROW 50001, 'Error message',1
    END TRY
    BEGIN CATCH
        SET @ErrorRaised = 1
    END CATCH

    EXEC tSQLt.AssertEquals 1,@ErrorRaised, 'Error raised'

    EXEC tSQLt.AssertEquals 0,1, '0=1'
END
GO

Вот пример, который проверяет конкретное сообщение об ошибке:

CREATE PROC MyTests.[test custom try catch test message]
AS
BEGIN
    DECLARE @ErrorRaised bit = 0

    BEGIN TRY
        THROW 50001, 'Error message',1
    END TRY
    BEGIN CATCH
        IF ERROR_MESSAGE() = 'Error message'
            SET @ErrorRaised = 1
        ELSE
            THROW
    END CATCH

    EXEC tSQLt.AssertEquals 1,@ErrorRaised, 'Error raised'

    EXEC tSQLt.AssertEquals 0,1, '0=1'
END
GO
person Ed Harper    schedule 09.08.2016

Для меня вызов как тестируемой процедуры, так и процедуры tSQLt ExpectException внутри блока TRY...CATCH, похоже, помог.

CREATE PROC MyTests.[test ExpectException]
AS
BEGIN
  BEGIN TRY
    -- Call the proc that raises the error
    EXEC offending_stored_proc

    -- Tell tSQLt to expect the exception
    EXEC tSQLt.ExpectException @ExpectedMessage = 'Expected error message', @ExpectedSeverity = NULL, @ExpectedState = NULL;
  END TRY
  BEGIN CATCH
    -- No need to do anything here
  END CATCH
END;

Как и ожидалось, если процедура вызывает ошибку с этим сообщением об ошибке, тест проходит успешно. Если ошибка не возникает, тест завершается неудачно.

person Stephan    schedule 29.06.2021