пятница, 19 ноября 2010 г.

Модульное тестирование при использовании SWI-Prolog

В императивных языках программирования, таких, например, как C#, Java или C++ программисты часто используют модульное тестирования. Суть подхода заключается в том, что сначала пишется тест для некоторой функциональной единицы (модуля), а после чего уже пишется код самого модуля. Мы просто поменяли процесс, если раньше мы сначала писали код, потом тестировали, то сейчас мы сначала пишем тест, потом пишем код, а потом тестируем (уже автоматизированно). Конечно, полная автоматизация тестирования это миф, но все же большую часть кода можно покрыть тестами.

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

Такой подход к разработке, называется разработка через тестирование (Test Driven Development) и я считаю, он себя оправдывает при написания проектов, где будет вноситься много изменений в код или проект, просто должен быть создан за короткие сроки. Не думайте, что вы потеряете время на написание тестов, пройдете время и вы поймете, что на самом деле вы только выигрываете.

Теперь о SWI-Prolog, в ПроЛоге мы с вами используем предикаты для реализации логики. И значит, тестировать мы будем предикаты. Идея такая: мы создаем предикат test_p, который тестирует наш предикат p. Как будет проходить тест?

Все очень просто. Для тестирования, мы будем задавать заранее известные входные параметры (термы) и сверять результат исчисления предиката с заранее известным правильным ответом. Если ответы совпали, все в порядке идем дальше, если нет выдаем информативное сообщение.

Для наглядности покажу простой пример, используя SWI-Prolog, заранее оговорюсь, эти же идеи применимы для любой другой версии компилятора ПроЛога.

Напишем предикат max(X, Y, Max), вычисляющий максимальное значение из 2-ух элементов, для начала опишем тест, затем реализацию:

% сперва напишем тест
test_max1 :- 
    max(3, 2, X), 
    X = 3 , !.
test_max1 :- 
    write('max(3, 2, X) вычислен не правильно!'),  
    nl.

test_max2 :- 
    max(2, 3, X), 
    X = 3, !.
test_max2 :- 
    write('max(2, 3, X) вычислен не правильно!'), 
    nl.

test_max3 :- 
    max(2, 2, X), 
    X = 2, !.
test_max3 :- 
    write('max(2, 2, X) вычислен не правильно!'), 
    nl.

run_tests :- 
    test_max1, 
    test_max2, 
    test_max3.
    
% теперь опишем предикат, max 
max(A, B, B) :- 
    A < B, 
    !.
max(A, _, A).

Запустим программу и вот, что увидим:



Изменим, чуть код, не важному почему, допустим случайно, или так надо было:

% ...    

% теперь опишем предикат, max 
max(A, B, B) :- 
    A > B, 
    !.
max(A, _, A).

И вот, что мы увидим в результате.



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

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

Комментариев нет:

Отправить комментарий