Предыдущая серия была тут - Вводная. Делаем тесты к скриптовым словам.
Там "слова словаря" содержались отдельно, а тесты к ним - отдельно.
Теперь я свёл всё это воедино:
string.ms.dict:
https://bitbucket.org/lulinalex/mindstream/src/4cc7f0347a37bc378109843cce5b316044fa22d4/Examples/Scripts/CodeGeneration1/string.ms.dict?at=B284
String.ms.script:
https://bitbucket.org/lulinalex/mindstream/src/4cc7f0347a37bc378109843cce5b316044fa22d4/Examples/Scripts/CodeGeneration1/String.ms.script?at=B284
Ну и тестовый вывод:
String.ms.script.out
Для того, чтобы свести всё воедино" я ввёл слова - TestsFor и RunTests.
TestsFor - определяет тесты для слова.
RunTests - запускает тесты для слова.
Выглядят они так:
Testing.ms.dict:
https://bitbucket.org/lulinalex/mindstream/src/4cc7f0347a37bc378109843cce5b316044fa22d4/Examples/Scripts/CodeGeneration1/Testing.ms.dict?at=B284
Ничего "космического".
Просто тесты "переехали" поближе к тому коду, который они тестируют.
Но! Это уже на самом деле - немало.
Это своего рода "инкапсуляция кода и контрактов к нему".
Код и тесты к нему - теперь находятся рядом.
Ведь тесты это своего рода "контракты к коду".
Работающие там, где не работает "статическая типизация".
Как когда-то правильно отметил Роман Янковский:
"В некотором смысле юнит-тесты решают абсолютно те же задачи, что и статическая типизация. Возможно, поэтому они наиболее прижились как раз там, где статической типизации нет.
Что такое статическая типизация? Это некое описание требований к коду, на соответствие котором компилятор будет наш код проверять. Что такое юнит-тесты? Это опять требования к коду, на соответствие которым код будет проверяться.
Если воспринимать юнит-тесты именно так, то исчезают все противоречия. С чего начинают разработку программы в языках со статической типизацией? С описания типов! С чего нужно начинать разработку программы в методологии TDD? С написания тестов! Так что это именно architecture first в обоих случаях.
Это выглядит не очевидно и разобщено, но во всем виноваты недостаточно развитые языковые средства, которыми мы пользуемся. Но тем не менее, в каком бы виде мы бы не описывали требования к входным и выходным данным некоторой функции, мы описываем ее ТИП и ничего более."
http://programmingmindstream.blogspot.ru/2013/11/tdd_28.html?showComment=1386063749826#c6337489882377572276
Вот собственно "эта идея" тут и развита.
Ну и "ничего нового в ней нет".
Похожие принципы описаны ещё у Барбары Лисков.
Я лишь "довёл идею до воплощения" - в одном отдельно взятом случае.
Ну и в заключение - одна простая мысль, которая мне лично - очень нравится.
Если код может быть протестирован просто, то он должен быть протестирован.
А если тесты находятся рядом с кодом или инкапсулированы в него, то это - здорово.
Потому что в таком случае тесты являются не только инструментом поверки, но и примерами использования кода, а также наглядной демонстрацией граничных условий.
Ну и если сделать "ещё один шаг", то из связки код-тесты-спецификация - можно увидеть исходные требования.
Ну и в конце хочу порекомендовать книгу:
"Использование абстракций и спецификаций при разработке программ
Барбара Лисков, Джон Гатэг"
"Описание
В книге американских специалистов излагаются основные способы создания спецификаций программ, повышающих эффективность разработки информационно-программного обеспечения. Значительное внимание уделено языку программирования CLU, позволяющему поддерживать различные типы абстракций, реализованных на языках ПЛ/1, Паскаль и Ада."
http://www.livelib.ru/book/1001241465
Это одна из моих любимых книг.
Там "слова словаря" содержались отдельно, а тесты к ним - отдельно.
Теперь я свёл всё это воедино:
string.ms.dict:
https://bitbucket.org/lulinalex/mindstream/src/4cc7f0347a37bc378109843cce5b316044fa22d4/Examples/Scripts/CodeGeneration1/string.ms.dict?at=B284
// string.ms.dict.web USES Documentation.ms.dict params.ms.dict core.ms.dict map.ms.dict Testing.ms.dict io.ms.dict ; : (string) ^ IN aValue aValue DO ToPrintable ; // (string) STRING FUNCTION string:CatWihAny STRING IN aString IN aValue aString aValue ToPrintable Cat =: Result ; // string:CatWihAny STRING FUNCTION any:Cat ARRAY IN anArray anArray .map> ToPrintable strings:Cat =: Result ; // any:Cat TestsFor any:Cat Test T1 [ 'A' 123 'B' ] any:Cat Print ; Test T2 [ 'A' 124 'B' ] any:Cat Print ; ; // TestsFor any:Cat STRING FUNCTION (+)? STRING in aPrefix STRING right aSuffix %SUMMARY 'Если aSuffix не пустой, то возвращает сумму aPrefix и aSuffix, иначе возвращает пустую строку' ; Result := '' STRING VAR l_Suffix aSuffix =: l_Suffix if ( l_Suffix =/= '' ) then ( aPrefix l_Suffix Cat =: Result ) ; // (+)? TestsFor (+)? Test T1 '' (+)? 'B' Print ; Test T2 'A' (+)? 'B' Print ; Test T3 'A' (+)? '' Print ; Test T4 'A' (+)? 'D' Print ; Test T5 'A' (+)? '123' Print ; ; // TestsFor (+)? STRING FUNCTION ?(+) STRING in aPrefix STRING right aSuffix %SUMMARY 'Если aPrefix не пустой, то возвращает сумму aPrefix и aSuffix, иначе возвращает пустую строку' ; Result := '' if ( aPrefix =/= '' ) then ( aPrefix aSuffix Cat =: Result ) ; // ?(+) TestsFor ?(+) Test T1 '' ?(+) 'B' Print ; Test T2 'A' ?(+) 'B' Print ; Test T3 'A' ?(+) '' Print ; ; // TestsFor ?(+) STRING FUNCTION strings:CatSep> STRING right aSep ARRAY right aValues aValues aSep strings:CatSep =: Result ; // strings:CatSep> TestsFor strings:CatSep> Test T1 strings:CatSep> ' ' [ 'A' 'B' ] Print ; Test T2 strings:CatSep> ' ' [ 'A ' 'B' ] Print ; Test T3 strings:CatSep> ' ' [ 'A ' ' B' ] Print ; Test T4 strings:CatSep> ' ' [ 'A' ' B' ] Print ; Test T5 strings:CatSep> ' ' [ '' 'B' ] Print ; Test T6 strings:CatSep> ' ' [ 'A' '' ] Print ; ; // TestsFor strings:CatSep> WordAlias CatSep> strings:CatSep>
String.ms.script:
https://bitbucket.org/lulinalex/mindstream/src/4cc7f0347a37bc378109843cce5b316044fa22d4/Examples/Scripts/CodeGeneration1/String.ms.script?at=B284
USES Documentation.ms.dict string.ms.dict Testing.ms.dict ; RunTests (+)? %REMARK 'Запускаем "стандартные тесты" для слова (+)?' RunTests ?(+) %REMARK 'Запускаем "стандартные тесты" для слова ?(+)' RunTests strings:CatSep> %REMARK 'Запускаем "стандартные тесты" для слова strings:CatSep>' RunTests any:Cat %REMARK 'Запускаем "стандартные тесты" для слова any:Cat'
Ну и тестовый вывод:
String.ms.script.out
Testing: (+)? T1 B T2 AB T3 T4 AD T5 A123 Testing end: (+)? ------------------ Testing: ?(+) T1 T2 AB T3 A Testing end: ?(+) ------------------ Testing: strings:CatSep> T1 A B T2 A B T3 A B T4 A B T5 B T6 A Testing end: strings:CatSep> ------------------ Testing: any:Cat T1 A123B T2 A124B Testing end: any:Cat ------------------
Для того, чтобы свести всё воедино" я ввёл слова - TestsFor и RunTests.
TestsFor - определяет тесты для слова.
RunTests - запускает тесты для слова.
Выглядят они так:
Testing.ms.dict:
https://bitbucket.org/lulinalex/mindstream/src/4cc7f0347a37bc378109843cce5b316044fa22d4/Examples/Scripts/CodeGeneration1/Testing.ms.dict?at=B284
// Testing.ms.dict USES axiom_push.ms.dict macro.ms.dict params.ms.dict io.ms.dict EngineTypes.ms.dict Documentation.ms.dict ; CONST cTests 'Tests:' MACRO TestsFor ENGINE_WORD RIGHT LINK IN aName %REMARK 'aName ссылка на слово справа от TestsFor' %SUMMARY 'Определяет набор тестов для слова aName' ; axiom:PushSymbol VOID axiom:PushSymbol axiom:operator cTests aName |N Cat Ctx:Parser:PushSymbol ; // TestsFor PRIVATE PROCEDURE DoRunTestsFor STRING IN aTestedWordName ENGINE_WORD IN aTestsHolder %SUMMARY 'Выполняет тесты для aTestsHolder' ; [ 'Testing: ' aTestedWordName ] strings:Cat Print aTestsHolder MembersIterator ==> ( IN aTest %REMARK 'aTest - вложенный элемент aTestsHolder' if ( ( aTest %ST |N ) = ( NameOf Test ) ) then %REMARK '- фильтруем только тесты.' begin aTest |N Print %REMARK 'Печатаем имя теста' aTest DO %REMARK 'Запускаем тест' end // ( ( aTest %ST |N ) = 'Test' ) ) [ 'Testing end: ' aTestedWordName ] strings:Cat Print '------------------' Print ; // DoRunTestsFor MACRO RunTests ENGINE_WORD RIGHT LINK IN aName %REMARK 'aName ссылка на слово справа от RunTests' %SUMMARY 'Выполняет тесты для aName' ; STRING VAR l_Name aName |N >>> l_Name STRING VAR l_TestsHolderName cTests l_Name Cat >>> l_TestsHolderName l_Name Ctx:Parser:PushString axiom:PushSymbol @ l_TestsHolderName Ctx:Parser:PushSymbol axiom:PushSymbol DoRunTestsFor ; // RunTests
Ничего "космического".
Просто тесты "переехали" поближе к тому коду, который они тестируют.
Но! Это уже на самом деле - немало.
Это своего рода "инкапсуляция кода и контрактов к нему".
Код и тесты к нему - теперь находятся рядом.
Ведь тесты это своего рода "контракты к коду".
Работающие там, где не работает "статическая типизация".
Как когда-то правильно отметил Роман Янковский:
"В некотором смысле юнит-тесты решают абсолютно те же задачи, что и статическая типизация. Возможно, поэтому они наиболее прижились как раз там, где статической типизации нет.
Что такое статическая типизация? Это некое описание требований к коду, на соответствие котором компилятор будет наш код проверять. Что такое юнит-тесты? Это опять требования к коду, на соответствие которым код будет проверяться.
Если воспринимать юнит-тесты именно так, то исчезают все противоречия. С чего начинают разработку программы в языках со статической типизацией? С описания типов! С чего нужно начинать разработку программы в методологии TDD? С написания тестов! Так что это именно architecture first в обоих случаях.
Это выглядит не очевидно и разобщено, но во всем виноваты недостаточно развитые языковые средства, которыми мы пользуемся. Но тем не менее, в каком бы виде мы бы не описывали требования к входным и выходным данным некоторой функции, мы описываем ее ТИП и ничего более."
http://programmingmindstream.blogspot.ru/2013/11/tdd_28.html?showComment=1386063749826#c6337489882377572276
Вот собственно "эта идея" тут и развита.
Ну и "ничего нового в ней нет".
Похожие принципы описаны ещё у Барбары Лисков.
Я лишь "довёл идею до воплощения" - в одном отдельно взятом случае.
Ну и в заключение - одна простая мысль, которая мне лично - очень нравится.
Если код может быть протестирован просто, то он должен быть протестирован.
А если тесты находятся рядом с кодом или инкапсулированы в него, то это - здорово.
Потому что в таком случае тесты являются не только инструментом поверки, но и примерами использования кода, а также наглядной демонстрацией граничных условий.
Ну и если сделать "ещё один шаг", то из связки код-тесты-спецификация - можно увидеть исходные требования.
Ну и в конце хочу порекомендовать книгу:
"Использование абстракций и спецификаций при разработке программ
Барбара Лисков, Джон Гатэг"
"Описание
В книге американских специалистов излагаются основные способы создания спецификаций программ, повышающих эффективность разработки информационно-программного обеспечения. Значительное внимание уделено языку программирования CLU, позволяющему поддерживать различные типы абстракций, реализованных на языках ПЛ/1, Паскаль и Ада."
http://www.livelib.ru/book/1001241465
Это одна из моих любимых книг.
Комментариев нет:
Отправить комментарий