Как проверить код, который никогда не должен выполняться?

Следующий метод должен вызываться только в том случае, если проверено наличие недопустимых цифр (путем вызова другого метода). Как я могу протестировать покрытие строки throw в следующем фрагменте? Я знаю, что одним из способов может быть объединение VerifyThereAreInvalidiDigits и этого метода. Я ищу любые другие идеи.

public int FirstInvalidDigitPosition {
    get {
        for (int index = 0; index < this.positions.Count; ++index) {
            if (!this.positions[index].Valid) return index;
        }
        throw new InvalidOperationException("Attempt to get invalid digit position whene there are no invalid digits.");
    }
}

Я также не хотел бы писать модульный тест, который выполняет код, который никогда не должен выполняться.

9
задан Rob 20 August 2010 в 23:35
поделиться

4 ответа

Если рассматриваемый оператор throw действительно недоступен при любом возможном сценарии, то его следует удалить и заменить на:

Debug.Fail("This should be unreachable; please find and fix the bug that caused this to be reached.");

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

ОБНОВЛЕНИЕ: согласно комментариям, на самом деле невозможно обнаружить ошибку, и поэтому код недоступен. Но теперь Debug.Fail также недоступен, и он не компилируется, потому что компилятор отмечает, что метод, возвращающий значение, имеет достижимую конечную точку.

Первая проблема не должна быть проблемой; Конечно, инструмент покрытия кода должен быть настраиваемым, чтобы игнорировать недоступный код, предназначенный только для отладки. Но обе проблемы можно решить, переписав цикл:

public int FirstInvalidDigitPosition 
{ 
    get 
    { 
        int index = 0;
        while(true) 
        {
            Debug.Assert(index < this.positions.Length, "Attempt to get invalid digit position but there are no invalid digits!");
            if (!this.positions[index].Valid) return index; 
            index++;
        } 
    }
}

Альтернативным подходом было бы реорганизовать код так, чтобы у вас вообще не было проблемы:

public int? FirstInvalidDigitPosition { 
    get { 
        for (int index = 0; index < this.positions.Count; ++index) { 
            if (!this.positions[index].Valid) return index; 
        } 
        return null;
    } 
} 

и теперь вам не нужно ограничивать вызывающие абоненты должны сначала вызвать AreThereInvalidDigits; просто сделайте законным вызов этого метода в любое время. Это кажется более безопасным. Методы, которые взрываются, когда вы не проводите дорогостоящую проверку, чтобы убедиться, что их можно вызывать, являются хрупкими и опасными.

22
ответ дан 4 December 2019 в 06:55
поделиться

Я не понимаю, почему вы не хотели бы писать модульный тест, который проверяет «код, которого не должно происходить».

Если этого «не должно происходить», то зачем вы вообще пишете код? Потому что вы думаете, что это, конечно, может произойти - возможно, ошибка в будущем рефакторинге нарушит вашу другую проверку, и этот код будет выполнен.

Если вы считаете, что код стоит писать, стоит его модульное тестирование.

4
ответ дан 4 December 2019 в 06:55
поделиться

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

Если у вас есть код, который должен никогда не выполняться, но может при правильных (или неправильных) условиях, вы можете убедиться, что исключение возникает в правильных условиях (в MS Unit Test Framework), украсив свой тестовый метод следующим образом:

[ExpectedException(typeof(InvalidOperationException))]
3
ответ дан 4 December 2019 в 06:55
поделиться

Иногда необходимо писать небольшие куски кода, которые не могут выполняться, только чтобы успокоить компилятор (например, если функция, которая всегда бросает исключение, выходит из приложения и т.д., вызывается из функции, которая должна возвращать значение, компилятор может настоять на том, чтобы вызывающая функция включала оператор "return 0", "Return Nothing" и т.д., который никогда не может выполняться. Я не думаю, что следует требовать тестирования такого "кода", поскольку может оказаться невозможным заставить его выполняться без серьезной поломки других частей системы.

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

  register = 0
  test register for zero
  if non-zero goto dead
  register = register - 1
  test register for zero
  if non-zero goto okay1
dead:
  shut everything down
  goto dead
okay1:
  register = register + 1
  if non-zero goto dead

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

4
ответ дан 4 December 2019 в 06:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: