26 нояб. 2014 г., в 16:29, Юрий Мироненко <assar...@gmail.com> написал(а):
--
--
http://groups.google.ru/group/sugr
---
Вы получили это сообщение, поскольку подписаны на группу "Russian Smalltalk User Group".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес sugr+uns...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.
--
Best regards,
Dennis Schetinin
Настройки подписки и доставки писем: https://groups.google.com/d/optout.
--
Best regards,
Dennis Schetinin
--
Best regards,
Dennis Schetinin
Еще вчера начал писать длинное письмо и даже с какими-то первыми шагами, но получилось очень долго и занудливо.
Первое, и самое важное: "тестировать"/"покрывать тестами" и TDD — совершенно разные вещи.
Соответственно, надо понять, что мы хотим: тестировать или разрабатывать.
Поэтому для начала надо взять какой-то один случай ("отщипнуть" от задачи маленький кусочек), специфицировать (вплоть до символа)— именно в первую очередь! — что ваш конвертер должен выдать, записать это в виде теста и реализовать.
В общем, если есть интерес, можем попробовать данную задачу с этой точки зрения решить. Мне самому интересно, получится что-то вменяемое или нет. Но это будет не быстро, учитывая необходимость объясняться и наличие свободного времени.
--
Best regards,
Dennis Schetinin
Книжка Бека про TDD прочитана? Там примеры вполне из жизни…
Раз, как я понимаю, "архитектура" уже выработана на случае XLSX, то этот вопрос не поднимаем.
В этом случае я бы записал в виде теста самый простой случай: пустой файл.
Я бы сгенерировал этот самый пустой файл, чтобы достать строку, которую он содержит. Она, возможно, будет не очень короткая, так что я бы ее в отдельный метод вытащил (expectedEmptyFileString).
Причём структура этих директорий и названия файлов не заданы жестко. Есть один (кажется) файл, в котором указаны имена и адреса остальных файлов, и что какой из остальных файлов значит. Но если я считаю этот файл, начну его парсить, а распарсив- скачаю другие файлы. То тогда я буду писать скорее импортер, чем тесты к экспортеру.
чтобы в справочнике клиентов было место рождения
--
Best regards,
Dennis Schetinin
Здесь для самого себя надо отделить что "мы тестируем быстро и модульно” и что "мы тестируем долго и полностью”.
Я бы для быстрых тестов не стал бы создавать workbook с worksheet-ами, потому как тестирование даже экспорта пустого документа - это значит тестировать работу в кооперации нескольких классов. Штук пяти не меньше.
Это не модульные тесты.
--
Best regards,
Dennis Schetinin
--
Best regards,
Dennis Schetinin
--
Best regards,
Dennis Schetinin
Все так плохо? :)
--
Best regards,
Dennis Schetinin
--
Best regards,
Dennis Schetinin
Если это не так — готов подробно прокомментировать.
OdsExporeterTests >> testGeneratesexporter generate should be: #someOdsFile
OdsExporeterTests >> testGenerates
[actualResult := exporter generate] should strictly satisfy:[
(zipper zip:(Set with: #rootFile with: #contentFile with: #styleFile))willReturn: #odsFile]
actualResult should be: #odsFile
block1 should strictly satisfy: block2.
[systemUnderTest exerciseMessage] should strictly satisfy: [mock someMessage].
[actualResult := systemUnderTest exerciseMessage] should strictly satisfy:[mock someMessage willReturn: #expectedResult].
actualResult should be: #expectedResult
[ :mock1 :mock2 |exerciseBlock should satisfy: expectationsBlock] runScenario
OdsExporeterTests >> testGenerates
[ :zipper |
exporter zipper: zipper.
[actualResult := exporter generate] should strictly satisfy:[
(zipper zip:(Set with: #rootFile with: #contentFile with: #styleFile))willReturn: #odsFile]
] runScenario.
actualResult should be: #odsFile
Как-то так…
--
Best regards,
Dennis Schetinin
--
То есть, решение принимается не исходя из каких-то априорных (с точки зрения текущей задачи) предпочтений, а из чисто практических побуждений.
--
Best regards,
Dennis Schetinin
LAD, Контроллер любой
Обнаружил случайно. Ставлю блок "Математика". Нажимаю (например) на конец вывод "А" мышкой. Одновременно левую и правую кнопку быстро и несколько раз.
Интерфейс вылетает.
Для того что бы использовать TDD надо обладать очень специфичным складом ума. Думать от обратного надо уметь <…> Наверное так можно писать когда четко знаеш что у тебя должно получится в конце.В реальной жизни же так почти никогда не бывает. В начале работы обычно не знаеш что получится на выходе и с чем столкнешся.
Алиса: – Куда мне отсюда идти?
Ч.Кот: — А куда ты хочешь попасть?
Алиса: — А мне все равно, только бы попасть куда-нибудь.
Ч.Кот: — Тогда все равно куда идти. Куда-нибудь ты обязательно попадешь.
Надо обладать специфичным складом ума, чтобы представлять, куда ты (скажем, впервые) направляешься, не имея возможности представить себе все нюансы маршрута?
TDD не предполагает какого-либо особого склада ума. Этот подход всего лишь заставляет в каждый момент времени понимать конечную цель своих действий — еще до начала этих самых действий.
2. Автоматические тесты - зло <…> 1… 2… 3…
Автомобили зло, потому что:
1. На них можно доехать не до любой точки
2. Чаще всего мы хотим прибыть в какое-либо помещение, а на автомобиле туда заехать невозможно
3. Автомобиль требует массу времени: его надо заправлять, заводить, ремонтировать.
Теперь, внимание, вопрос: а какое все это отношение имеет к автоматизации?
ввести тестирование на проекте
Не устану повторять: TDD и тестирование — принципиально разные вещи. Как автоматизация и автомобиль — даже несмотря на то, что у них общий корень "авто".
TDD не предполагает какого-либо особого склада ума. Этот подход всего лишь заставляет в каждый момент времени понимать конечную цель своих действий — еще до начала этих самых действий.
isNeededMicrosTimerUtilityFunction
isNeededMicrosTimerUtilityFunction
^self onConstantIsMicros.
onConstantIsMicros
onConstantIsMicros
onConstantIsMicros
isNeededMicrosTimerUtilityFunction
(self isSingle or: [self isSinhroMulty])
ifTrue: [^self onConstantIsMicros].
^self onConstantIsMicros or: [self offConstantIsMicros]
--
Вы получили это сообщение, поскольку подписаны на одну из тем в группе "Russian Smalltalk User Group".
Чтобы отменить подписку на эту тему, перейдите по ссылке https://groups.google.com/d/topic/sugr/4ibrvXynivs/unsubscribe.
Чтобы отменить подписку на эту группу и все ее темы, отправьте письмо на электронный адрес sugr+uns...@googlegroups.com.
isNeededMicrosTimerUtilityFunction
(self isSingle or: [self isSinhroMulty])
ifTrue: [^self onConstantIsMicros].
^self onConstantIsMicros or: [self offConstantIsMicros]
AbstractBlock
AbstractLADElement
LADManyInputsBlock
LADBlocksWithoutVariables
LADAnalogSwith
LADRealTimeClockSetTimeBlock
LADScaleBlock
LADBounceBlock
LADCounterBlock
LADStepMotorBlock
LADCommPortBlock
LADDisplayOnChipHD44780Block
LADRealTimeClockAlarmBlock
LADRealTimeClockGetTime
LADArithmeticBlock
LADServoMotorBlock
LADStandartFunctionBlock
LADABSBlock
LADCosBlock
LADMAXBlock
LADMINBlock
LADPOWBlock
LADRANDOMBlock
LADSinBlock
LADSQBlock
LADSQRTBlock
LADTanBlock
LADUltrasonicBlock
LADIrRessiveBlock
LADDisplayOnChipHD4480I2CBacklightControlBlock
LADAbstractSDBlocks
LADSaveToSDVariableBlock
LADUploadFileFromSDBlock
LADConvertFloatToIntegerBlock
LADBlocksWithTypeAndUpdateTime
LADBMP085Block
LADDigitalTemperatureAndHumidityBlock
LADDS18x2xBlock
LADChip74HC595Block
LADSevenSegmentDecoderBlock
LADManyInputsSwithBlock
LADManyOutputsSwithBlock
LADBitCoderBlock
LADBitDecodeBlock
LADBitReadeBlock
LADBitWriteBlock
LADMatrixKeyboardBlock
LADPiezoSpeakerBlock
LADReadFromEepromBlock
LADSaveToEepromBlock
LADSendRessiveVariableFromCommunication
LADRessiveVariableFromCommunication
LADSendVariableFromCommunication
LADWebServises
LADWebServerPage
LADWebClient
LADSummStringBlock
LADSpecialCoil
LADCompareCoil
LADTriggerCoil
LADTimerCoil
LADGenCoil
LADConvertStringBlock
LADBasicElement
LADContact
LADCoil
LadRtrigCoil
LADAnalogInputOutputBlock
LADAnalogConductorInput
LADAnalogInput
LADAnalogConductoOutput
LADAnalogOutput
LADSendRessiveVariablesBlocks
LADRessiveVariablesBlocks
LADSendVariableBlock
LADSpeedCounterBlock
AbstractElement
BlocksWithType
BlocksWithTypeAndCollectionOutputs
BlocksWithTypeAndCollectionInputsOutputs
FBDSwitchBlock
FBDArithmeticBlock
FBDBounceBlock
FBDSummStringBlock
FBDTimerBlock
FBDServoMotorBlock
FBDBasicLogikBlock
FBDCommPortBlock
FBDCompareBlock
FBDTriggerBlock
ALADAnalogConductoOutput
FBDStandartFunctionBlock
FBDConvertStringBlock
FBDGeneratorBlock
CounterBlock
FBDStepMotorBlock
FBDDisplayOnChipHD44780Block
FBDRealTimeClockAlarmBlock
FBDRealTimeClockSetTimeBlock
FBDRealTimeClockGetTimeBlock
FBDScaleBlock
FBDUltrasonicBlock
FBDIrRessiveBlock
FBDDisplayOnChipHD4480I2CBacklightControlBlock
FBDAbstractSDBlocks
FBDSaveToSDVariableBlock
FBDUploadFileFromSDBlock
FBDConvertFloatToIntegerBlock
FBDBlocksWithTypeAndUpdateTime
FBDDigitalTemperatureAndHumidityBlock
FBDDS18x2xBlock
FBDBMP085Block
FBDChip74HC595Block
FBDSevenSegmentDecoderBlock
FBDManyInputsSwithBlock
FBDManyOutputsSwithBlock
FBDBitCoderBlock
FBDBitDecodeBlock
FBDBitReadBlock
FBDBitWriteBlock
FBDMatrixKeyboardBlock
FBDPiezoSpeakerBlock
FBDSaveToEepromBlock
FBDReadFromEepromBlock
FBDWebServises
FBDWebServerPage
FBDWebClient
FBDNarodMonRuClient
TagBlock
SendRessiveVariablesBlocks
SendVariableBlock
RessiveVariablesBlocks
SpeedCounterBlock
FBDSendRessiveVariableFromCommunication
FBDRessiveVariableFromCommunication
FBDSendVariableFromCommunication
Возможно в небольших проектах и можно четко представлять себе конечную цель. Заранее продумать всю архитектуру, состав классов и взаимодействие между ними.
--
Best regards,
Dennis Schetinin
--
Но тут возникает очевидный вопрос: уж если есть таблица истинности, то проще считать её реализацией метода, а не его проверкой.
--
Best regards,
Dennis Schetinin
По результатам моего небольшого эксперимента он жестко ограничивает свободу разработчика, при этом являясь огромным тормозом в скорости разработки.
...При сохранении проекта программа падает. Правильно говорят что торопится нельзя и надо полнее производить проверку программы.
--
P.S. Конечно, я понимаю, что TDD и "просто тестирование" - вещи разные. Но мне казалось, что критика Сергея относилась не только к TDD, но и к тестам вообще. Хотя бы потому, что в конце повествования он снёс SUnit :)
В основной массе ошибки возникают в новых блоках по достаточно ограниченному ряду причин. У менея даже есть небольшой список что надо обязательно сделать при создании нового блока, но все равно время от времени на этом прокалываюсь. Щсновная из причин - не полная реализация метода postCopy. Где то 80% ошибок идут отдуда. Забываю реализовать либо копирование обекта в переменной , либо реализовать PostCopy у самого нового обекта. И тут никакое тестирование не поможет. Если я вспомню о необходимости написать тест на копирование переменной, то мне быстрее написать одну строчку на копирование чем тест.
И самое главное что это не будет менятся в будущем, поэтому проверять не имеет смысла.
--
Best regards,
Dennis Schetinin
13 янв. 2015 г., в 7:06, Сергей Глушенко <sup...@flprog.ru> написал(а):
--
Best regards,
Dennis Schetinin
isCorrect
super isCorrect ifFalse: [^false].
pinNumber ifNil: [^false].
self isUseCountInTimeFunction ifTrue: [countTime ifNil: [^false]].
self isUsePulseDurationFunction
ifTrue:
[(self hasPositivDurationOutput or: [self hasNegativeDurationOutput])
ifFalse: [^false]].
self isUseDelayLineFunction
ifTrue:
[self hasDelayTimeInput ifFalse: [self delayTimeConstant ifNil: [^false]].
self hasDestinationOutput
ifFalse: [self controllerDestinationpinNumber ifNil: [^false]]].
^true
--
Best regards,
Dennis Schetinin
isUseCountInTimeFunction
и countTime выделить в отдельную сущность
также относиться и к
- (isUsePulseDurationFunction
hasPositivDurationOutput
hasNegativeDurationOutput)
-
(isUseDelayLineFunction
hasDelayTimeInput
delayTimeConstant
hasDestinationOutput
controllerDestinationpinNumber
controllerDestinationpinNumber
)
в этих объектах (сущностях) реализовать метод isCorrect или использовать callback методы (например, сложение разных типов чисел) где сначала идет общий метод сложения, далее у разных типов чисел через уточняющие методы происходит сложение
isUseCountInTimeFunction
isUseCountInTimeFunction ifNotNil: [^isUseCountInTimeFunction].
isUseCountInTimeFunction := true.
^isUseCountInTimeFunction
isNeededPositiveInterruptFunction
self hasInputValueOutput ifTrue: [^true].
self isUseDelayLineFunction
ifTrue: [self hasDelayPositiveFront ifTrue: [^true]].
self isUsePulseDurationFunction ifTrue: [^true].
(self isUseCountPulseFunction and: [self isUsePositiveFrontForPulseCount])
ifTrue: [^true].
^false
isSpeedCounter
^true
hasDestinationOutput
^destinationOutput isNil not
canConnectOutput: aArduinoFBDInputOutput to: aArduinoFBDConnection
aArduinoFBDInputOutput isBooleanType ifTrue:[^aArduinoFBDConnection isBooleanType].
^aArduinoFBDConnection isNumberType
--
Best regards,
Dennis Schetinin
--
Best regards,
Dennis Schetinin
Вопрос на засыпку. А зачем?
У меня есть штук 5 вариантов ответа на этот вопрос, но наверное будет некорректно влезать перед автором рац. предложения :)
Еще вопрос: метод isCorrect и подобные (я, так понимаю, они есть) с развитием системы уже никогда не будут больше меняться?
Нигде кроме данного класа они не потребуются.
isCorrect
super isCorrect ifFalse: [^false].
connectionDevice ifNil: [^false].
connectionDevice isCorrect ifFalse: [^false].
^true
Это блок WebServer Здесь девайсом могут выстумать несколко различных шидлдов. Типа вайфай, W5100 и другие. На сегодня их три вида. Кроме этого блока данные классы являющиеся моделями устройства применяются еще в нескольких видах блоков, и количество блоков с применением этих моделей будет расти, как и количество самих моделей. Только WIFI модулей под ардуинку видов десять. И каждый работает по своему, Каждый имеет свой набор параметров. Блоки их использующие меняться больше не будут. Им глубоко плевать какую железяку в них засунули. Они хранят информацию о странице которую нуэно отправить на запрос к серверу. Каждая железяка знает о своем SubFrame с набором параметров для себя. Соответственно описанная и отлаженная железяка то же менятся больше не будет. Она умеет дернуть свой кусок компилятора создающего код обеспечивающий ее работу. Соответственно кусок кода компилятора(метод или набор методов) отвечающий за ее программирование то же отладится один раз и забудется. При появлении у меня новой железяки просто будет написан новый класс модели железяки с тс общим интерфейсом. и таких железяк у меня уже много
AbstractExtendDevice
AbstractDisplay
DisplayOnChipHD44780
StepMotor
AbstractRealTimeClock
DS1302RealTimeClock
DS1307RealTimeClock
ServoMotor
UltrasonicHC_SR04
DigitalTemperatureAndHumidity
OneWire
DS18x2x
IRRessiver
BMP085
SevenSigmentIndicatorStatic
SevenSigmentIndicatorDinamic
Chip74HC595
EthernetDevice
EthernetShild
ENC28J60EthernetShild
W5100EthernetShild
ESP8266WiFiModule
SDCardReader
Так что поверте что с композицией я то же умею работать. Просто нельзя зацикливаться на одном патерне, а в каждом конкретном случае надо находить компромис, приносящий оптимальный результат. Кстати как я и говорил раньще если классы тех же блоков отлаживаются один раз и забываются, то и тестировать их нет необходимости. То же касается и моделей железа
--
Best regards,
Dennis Schetinin
--
checkProject
| errors stream |
projectFrame ifNil: [^self].
errors := self currentProject errors.
errors isEmpty ifTrue: [^Dialog rusButtonWarn: 'Проект корректен'].
stream := String new writeStream.
errors do: [:each | stream nextPutAll: each]
separatedBy: [stream nextPut: Character cr].
Dialog rusButtonWarn: stream contents
errors
^programm errors
errors
| result |
result := OrderedCollection new.
networks doWithIndex:
[:each :idx |
result
addAll: (each errors collect:
[:eachError |
eachError
expandMacrosWithArguments: (OrderedCollection with: idx printString)])].
^result
errors
| notCorrectBlocks result |
notCorrectBlocks := blocks select: [:each | each isCorrect not].
notCorrectBlocks isEmpty ifTrue: [^#()].
result := OrderedCollection new.
result add: 'На плате №<1s> - ' , notCorrectBlocks size printString
, (notCorrectBlocks size = 1
ifTrue: [' некорректный блок']
ifFalse:
[notCorrectBlocks size > 4
ifTrue: [' некорректных блоков']
ifFalse: [' некорректных блока']]).
^result
--
Best regards,
Dennis Schetinin
negativeFrontInterrupFunctionForCountInTimeFunctionWithBlock: aBlock
| stream |
aBlock isUseCountInTimeFunction ifFalse: [^''].
stream := String new writeStream.
stream
nextPutAll: aBlock declaringImpulseCountName;
nextPutAll: '++;';
nextPut: Character cr.
^stream contents
Ну все для меня на сегодня дебаты закончены.
А где Вы увидали размазаность?
блок просто спрашивает у устройства какой фрейм отрисовать на соответвующей субканве создает данный фрейм и отдаее ему устройство в качестве модели