Моделирование требований и поведения
Вопрос построения моделей требований и поведения является очень непростым, как и всякий вопрос, связанный с переходом от неформальной сущности к формальной модели. Для целого ряда частных случаев можно представить правила и рекомендации по построению моделей. Тем не менее, в общем случае решение этой задачи во многом основывается на опыте и интуиции разработчика тестов.
Для иллюстрации взаимосвязи моделей требований и поведения с реальными объектами, рассмотрим один из вариантов построения этих моделей в том случае, когда целевая система представляет собой систему с прикладным программным интерфейсом.
Предположим, что целевая система предоставляет своим пользователям прикладной программный интерфейс, состоящий из n функций. Каждая интерфейсная функция имеет набор входных и выходных параметров. Кроме того, вызов интерфейсных функций может изменять внутреннее состояние целевой системы или ее окружение, и таким образом влиять на результаты следующих вызовов интерфейсных функций.
Тогда для каждой функции прикладного интерфейса можно определить соответствующую ей интерфейсную операцию. Сигнатура интерфейсной операции определяется по следующим правилам:
- входные параметры интерфейсной операции и их типы соответствуют входным параметрам функции прикладного интерфейса,
- выходные параметры интерфейсной операции и их типы соответствуют выходным параметрам функции прикладного интерфейса,
- переменные состояния соответствуют тем частям внутреннего состояния целевой системы и/или ее окружения, от которого зависит ожидаемое поведение функции прикладного интерфейса. Мы здесь использовали слово "ожидаемое", чтобы отметить, что в данном случае нас интересует поведение не конкретной реализации целевой системы, а поведение ожидаемое от нее пользователем.
Требования к поведению целевой системы после вызова функции прикладного интерфейса описывается в виде спецификации соответствующей интерфейсной операции. Таким образом, мы задаем модель требований к целевой системе посредством спецификации, состоящей из спецификаций отдельных интерфейсных операций.
Определив сигнатуры интерфейсных операций, участвующих в спецификации, мы тем самым определили интерфейс целевой системы ( X, Y, V ). Согласно MR[Spec] этот интерфейс определяется следующим образом:
- множество стимулов X состоит из вызовов функций прикладного интерфейса со всеми допустимыми наборами входных параметров,
- множество реакций Y состоит из всех возможных возвращаемых значений интерфейсных функций, где под возвращаемым значением подразумевается кортеж значений выходных параметров данной функции,
- множество состояний V состоит из значений, соответствующих различным значениям внутреннего состояния целевой системы и/или ее окружения. При этом учитываются только те значения, от которых зависит ожидаемое поведение хотя бы одной функции прикладного интерфейса.
Взаимодействием с целевой системой в этом случае является четверка ( v, x, y, v' ), в которой:
- пресостояние v соответствует состоянию целевой системы и/или ее окружения до вызова функции прикладного интерфейса,
- стимул x соответствует вызову функции прикладного интерфейса с определенным набором значений входных параметров,
- реакция y соответствует набору значений выходных параметров, который вернула вызванная функция,
- постсостояние v' соответствует состоянию целевой системы и/или ее окружения, в котором она оказалась после данного вызова.
Соответственно, модель поведения целевой системы состоит из множества таких четверок и строится тестовой системой в ходе тестирования.
Рассмотрим данный подход на примере системы, реализующей функциональность целочисленного стека. Прикладной программный интерфейс этой системы состоит из трех функций:
- функция помещения числа в стек имеет один входной параметр - число, и не имеет выходных параметров,
- функция выборки элемента с вершины стека не имеет входных параметров и имеет один выходной параметр - число, находящееся на вершине стека,
- функция получения размера стека не имеет входных параметров и имеет один выходной параметр - число, равное текущему размеру стека.
Для определенности будем считать, что целые числа, участвующие в этом примере, заданы множеством Int.
Для каждой функции прикладного программного интерфейса определим соответствующую ей интерфейсную операцию.
Сигнатуру операции помещения числа в стек определим как ( { x }, {}, { vstack } ), где тип входного параметра x - Int, а переменной состояния vstack - Int-list. Спецификацию этой операции ( prepush, postpush ) зададим следующими предикатами:
prepush ( vstack, x ) ≡ true
postpush ( vstack, x, vstack' ) ≡ ( vstack' = x ° vstack )
Сигнатуру операции выборки элемента с вершины стека определим как ( {}, { y }, { vstack } ), где тип выходного параметра y - Int, а переменной состояния vstack - Int-list. Спецификацию этой операции ( prepop, postpop ) зададим следующими предикатами:
prepop ( vstack, y ) ≡ size( vstack ) > 0
postpop ( vstack, y, vstack' ) ≡ ( y = head( vstack ) ) ( vstack' = tail( vstack ) )
Сигнатуру операции получения размера стека определим как ( {}, { y }, { vstack } ), где тип выходного параметра y - Int, а переменной состояния vstack - Int-list. Спецификацию этой операции ( presize, postsize ) зададим следующими предикатами:
presize ( vstack, y ) ≡ true
postsize ( vstack, y, vstack' ) ≡ ( y = size( vstack ) ) ( vstack' = vstack )
Спецификации этих трех операций составляют спецификацию системы, реализующей функциональность целочисленного стека. На основе данной спецификации мы можем построить модель требований, как это определено в MR[Spec]. Наиболее важным для нас является интерфейс целевой системы, который определяется неявным образом при построении модели требований.
В данном случае интерфейс целевой системы есть ( Xstack, Ystack, Vstack ), где:
- Xstack = Int { pop } { size } - множество стимулов состоит из
- целых чисел, соответствующих вызову функции помещения числа в стек с данным числом в качестве параметра,
- элемента pop , соответствующего вызову функции выборки элемента с вершины стека,
- элемента size , соответствующего вызову функции получения размера стека,
- Ystack = { push } Int Int - множество реакций состоит из
- элемента push , соответствующего отсутствующим выходным параметрам функции помещения числа в стек,
- целых чисел, соответствующих значению единственного выходного параметра функции выборки элемента с вершины стека,
- целых чисел, соответствующих значению единственного выходного параметра функции получения размера стека,
- Vstack = Int-list - множество состояний состоит из списков целых чисел, соответствующих текущему состоянию стека (первый элемент списка соответствует вершине стека).
Рассмотрим, как будет выглядеть модель поведения целевой системы для одного из частных случаев. Предположим, что в ходе тестирования мы вызвали целевую систему четыре раза:
- вызвали функцию помещения числа в стек с параметром 0, когда стек был пустым, и получили стек, содержащий число 0;
- вызвали функцию получения размера стека и получили 1, в качестве значения выходного параметра, значение стека не изменилось;
- вызвали функцию выборки элемента с вершины стека и получили 0, в качестве значения выходного параметра, значение стека не изменилось;
- вызвали функцию получения размера стека и получили 1, в качестве значения выходного параметра, значение стека не изменилось.
Модель поведения для данного теста будет состоять из четырех взаимодействий:
{
( , 0, push, 0 ),
( 0 , size, 1, 0 ),
( 0 , pop, 0, 0 ),
( 0 , size, 1, 0 )
}
Если описать эту модель в терминах спецификации целевой системы, то мы получим следующее:
{
( vstack , push (0 ), push (), vstack = 0 ),
( vstack 0 , size (), size (1 ), vstack = 0 ),
( vstack 0 , pop (), pop (0 ), vstack = 0 ),
( vstack 0 , size (), size (1 ), vstack = 0 )
}
Пресостояние и постсостояние задается значением переменной состояния vstack, стимул записывается в виде идентификатора интерфейсной операции со списком значений ее входных параметров, а реакция - в виде идентификатора интерфейсной операции со списком значений ее выходных параметров.