AXForum  
Вернуться   AXForum > Прочие обсуждения > Курилка
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 14.03.2017, 17:08   #41  
trud is offline
trud
Участник
 
340 / 212 (8) ++++++
Регистрация: 07.06.2003
Цитата:
Сообщение от Logger Посмотреть сообщение
Написать метод проверяльщик.
Проблему с датой не вижу. Ее тоже можно выставить, даже из интерфейса.
вопрос в разумности времени. т.е. вот из соседней ветки ошибка, т.е. попортили разноску при выпуске CU12. во сколько вы бы оценили сценарий авто-тестирования который бы позволял выявлять такие вещи? этож человеко годы

Цитата:
CU12 Known Issue – Incorrect Posting amounts – Shipment Packing slip posting
Known scenarios
The released product is associated with an item model group where Post physical inventory to ledger = True or Post to Deferred Revenue Account on sales delivery = True, and
Sales order line lot ID is split based on output orders and grouped in the same shipment, and
Sales packing slip is posted from Inventory management>Common>Shipments.
Старый 14.03.2017, 17:52   #42  
Андре is offline
Андре
Moderator
Сотрудники компании GMCS
 
2,371 / 444 (19) +++++++
Регистрация: 03.12.2001
Цитата:
вообще говоря, unit-тестирование предполагает, что все запуски выполняются в одном и том же окружении. поэтому при правильном подходе "вокруг" ничего меняться не должно.
Извини, но, насколько я понимаю, unit-тестирование предполагает, что никакого окружения, кроме тестируемого в данный момент кода, вообще нет.
Старый 14.03.2017, 17:56   #43  
Андре is offline
Андре
Moderator
Сотрудники компании GMCS
 
2,371 / 444 (19) +++++++
Регистрация: 03.12.2001
Цитата:
да, я услышал, что в этой ветке говорилось только о регрессии.
и таки да, наверное вряд ли стоит ожидать чего-то другого от тестирования в коде.
Нет, слово "регрессия" услышал ты. Наверное потому что хотел его услышать. Я просто не стал спорить.

Я же правильно понимаю, что слово "регрессия" ты используешь в контексте сравнения текущего состояния с каким то предыдущим ?

Цитата:
Сообщение от Андре Посмотреть сообщение
Вообще, целью unit тестирования для меня является минимальная гарантия того, что я смогу сегодня ночью спокойно спать и меня не разбудят со словами, что что-то отвалилось.

другими словами, контроль регрессии.
пусть так.
Для меня unit test-ы это еще одна проверка на то, что код ведет себя именно так, как я ожидаю. В том числе и только что написанный. Это не значит что он работает правильно. И не значит, что ничего не поломалось. Это значит что мое представление о коде более-менее соответствует действительности.

p.s. Еще мне нравятся unit-test-ы как средство документирования проекта/библиотеки. Лучший способ понять, как использовать какую-то библиотеку - посмотреть ее unit тесты.

Последний раз редактировалось Андре; 14.03.2017 в 18:00.
Старый 14.03.2017, 21:08   #44  
ALES is offline
ALES
Участник
Злыдни
 
202 / 41 (2) +++
Регистрация: 11.08.2004
Цитата:
Сообщение от mazzy Посмотреть сообщение
вообще говоря, unit-тестирование предполагает, что все запуски выполняются в одном и том же окружении. поэтому при правильном подходе "вокруг" ничего меняться не должно..
Оно и "не меняется", но своей функциональностью плодит "множество" конкретных значений параметра для "проверки" (и IMHO выбран изначально неудачный пример "проектирования" метода под юнит-тест, т.к. влияющие на логику параметры поступают в "зашифрованном" с потерей информации виде)


Цитата:
Сообщение от mazzy Посмотреть сообщение
поэтому формально - именно "зеленый квадратик".
тогда формально он будет при - "все возможные комбинации" при запуске для каждый из параметров = "по умолчанию"\"не по умолчанию" дают "зеленый" результат
Старый 14.03.2017, 21:43   #45  
mazzy is offline
mazzy
Administrator
Аватар для mazzy
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
Most Valuable Professional
 
20,274 / 3072 (143) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Цитата:
Сообщение от ALES Посмотреть сообщение
(и IMHO выбран изначально неудачный пример "проектирования" метода под юнит-тест, т.к. влияющие на логику параметры поступают в "зашифрованном" с потерей информации виде)
да, изначально выбран метод, который существует с лохматых времен аксапты, который изначально не проектировался под юнит-тесты. и который демонстрирует общий аксапта-стиль )

именно.
и как правильно выполнять unit-тестирование таких методов?

да, именно так вопрос и был поставлен.


Цитата:
Сообщение от ALES Посмотреть сообщение
тогда формально он будет при - "все возможные комбинации" при запуске для каждый из параметров = "по умолчанию"\"не по умолчанию" дают "зеленый" результат
всех возможных значимых комбинаций 2^8 = 256



Цитата:
Сообщение от Андре Посмотреть сообщение
Я же правильно понимаю, что слово "регрессия" ты используешь в контексте сравнения текущего состояния с каким то предыдущим ?
да
https://ru.wikipedia.org/wiki/%D0%A0...BD%D0%B8%D0%B5


Цитата:
Сообщение от Андре Посмотреть сообщение
p.s. Еще мне нравятся unit-test-ы как средство документирования проекта/библиотеки. Лучший способ понять, как использовать какую-то библиотеку - посмотреть ее unit тесты.
ну да, ну да. юнит тесты, как отображение спецификации на код исполняющей системы.

это точно сейчас не присутствует.
но с удовольствием послушаю размышления применительно к аксапте.

Цитата:
Сообщение от Андре Посмотреть сообщение
Для меня unit test-ы это еще одна проверка на то, что код ведет себя именно так, как я ожидаю. В том числе и только что написанный. Это не значит что он работает правильно. И не значит, что ничего не поломалось. Это значит что мое представление о коде более-менее соответствует действительности.
ок.
можешь рассказать об этом применительно к методам с параметрами по умолчанию?
каковы критерии необходимости и достаточности?
__________________
Facebook, mazzy.priot, mazzy.music.
Старый 14.03.2017, 22:33   #46  
Андре is offline
Андре
Moderator
Сотрудники компании GMCS
 
2,371 / 444 (19) +++++++
Регистрация: 03.12.2001
Цитата:
можешь рассказать об этом применительно к методам с параметрами по умолчанию?
А я вообще не понял, что тебе сдались эти параметры по умолчанию. Что нового они приносят в плане тестирования? Этот вопрос был и выше, но я не понял твой ответ на него.

На мой взгляд ничего. Методы с параметрами по умолчанию надо тестировать точно также, как и методы с обычными параметрами. Все вот эти вопросы:

Цитата:
например, сколько тестирующих методов должно быть для метода с дефолтными параметрами?
= один тестирующий с несколькими ассертами?
= столько тестирующих, сколько различных комбинаций параметров для того, чтобы покрыть все значимые комбинации параметров? причем в каждом тестирующем методе должен быть только один ассерт?
= какое-то "достаточное" число test-методов? каков критерий достаточности?
должны возникать и в том случае, если метод и без дефолтных параметров. Более того, и ответы на них вроде будут те же самые.

Цитата:
каковы критерии необходимости и достаточности?
Здравый смысл.

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

Многие фреймворки для тестирования позволяют все возможные case-ы для проверки записывать в лаконичном табличном виде в виде комментариев и по ним уже генерить вызовы методов тестирования.Как то так:

PHP код:
import spock.lang.Specification
 
class MathSpec extends Specification {
 
    
def "Get the max value of two numbers"() {
 
        
expect'Should return the bigger number'
        
Math.max(ab) == c
 
        where
:
        
c
        1 
1
        2 
3
    
}

Есть фреймворки, которые позволяют случайно генерить данные для тестирования (https://github.com/fscheck/FsCheck). Как то так:

PHP код:
let revRevIsOrig (xs:list<int>) = List.rev(List.rev xs) = xs
Check
.Quick revRevIsOrig 
Но я не сторонник таких подходов.

Попробую объяснить. Этой мой unit test и это мой код, который я тестирую. Я его знаю. Я могу быть не уверен, работает ли он корректно, но подразумевается, что я достаточно хорошо его понимаю, чтобы выделить какие-то сценарии его использования (как стандартные, так и наоборот - неожиданные). Их и надо тестировать.

Последний раз редактировалось Андре; 14.03.2017 в 22:55.
Старый 14.03.2017, 23:08   #47  
mazzy is offline
mazzy
Administrator
Аватар для mazzy
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
Most Valuable Professional
 
20,274 / 3072 (143) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Цитата:
Сообщение от Андре Посмотреть сообщение
А я вообще не понял, что тебе сдались эти параметры по умолчанию. Что нового они приносят в плане тестирования?
ничего.
кроме того, что это фишка аксапты, если я правильно понимаю.
поэтому ответ на вопрос не гуглится на стековерфлоу )))

Цитата:
Сообщение от Андре Посмотреть сообщение
должны возникать и в том случае, если метод и без дефолтных параметров. Более того, и ответы на них вроде будут те же самые.
угу.

Цитата:
Сообщение от Андре Посмотреть сообщение
Здравый смысл.
понятно.

Цитата:
Сообщение от Андре Посмотреть сообщение
Многие фреймворки для тестирования позволяют все возможные case-ы для проверки записывать в лаконичном табличном виде в виде комментариев и по ним уже генерить вызовы методов тестирования. Есть фреймворки, которые позволяют случайно генерить данные для тестирования (https://github.com/fscheck/FsCheck). Но я не сторонник таких подходов.
А почему не сторонник?

есть такие фреймоврки.
но в аксапте их нет.
стоит ли добавлять в аксапту?
стоит ли писать свой велосипед для подобных случаев?


Цитата:
Сообщение от Андре Посмотреть сообщение
Попробую объяснить. Этой мой unit test и это мой код, который я тестирую. Я его знаю. Я могу быть не уверен, работает ли он корректно, но подразумевается, что я достаточно хорошо его понимаю
А вот тут похоже и есть корень.
В аксапте чаще наоборот. И об этом писал fed выше.

Мой код использует что-то из стандартного функционала аксапты.
как правило, я не до конца понимаю как работает стандарт на всех граничных значениях.
как правило, я не понимаю нафига сделано именно так.

мало того, как правило, мой код всего лишь расширяет поведение какого-то стандартного метода.

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

и в этих условиях я должен изобразить что-то "сторожевое". )))
ну, т.е. понятно, что завсегда можно использовать "тяп-ляп и продакшн".
понятно, что каждую конкретную задачу решить можно.

вопрос то: как правильно? )))
пока есть ответ "Здравый смысл"

=================
не уверен, что понадобиться, но вот полный код метода.
повторюсь, что это очень и очень давний метод, который можно проследить до самых первых версий аксапты, в которых он был простым как пареная репа.
X++:
private boolean  findDisc(PriceType             _relation,
                              InventDimId           _inventDimId,
                              TableGroupAll         _itemCode       =  0,
                              ItemId                _itemRel        = '',
                              TableGroupAll         _accountCode    =  0,
                              CustVendAC            _accountRel     = '',
                              UnitOfMeasureSymbol   _unitID         = '',
                              Qty                   _quantityAmount =  0,
                              CurrencyCode          _currency       = CompanyInfoHelper::standardCurrency(),
                              AgreementHeaderExtRecId_RU _agreementHeaderExtRecId = 0,
                              CustVendAC                 _agreementPartnerCode = '')
    {
        PriceDiscTable      priceDiscTable;
        boolean             discExist;
        container           key;
        container           cacheValue;
        int                 i;
        FromDate            localFromDate;
        ToDate              localToDate;
        AmountQty           localQuantityAmountFrom;
        AmountQuantityTo    localQuantityAmountTo;
        RecId               localRecid;
        boolean             cacheMode;
        // <GEERU>
        CustVendAC      accountRelation = _accountRel;
        // </GEERU>
        void reselectBuffer()
        {
            if (cacheMode)
            {
                priceDiscTable = PriceDiscTable::findRecId(localRecid);
            }
        }

        void findDisc()
        {
            if ((discDate >= localFromDate  || ! localFromDate)
                && (discDate <= localToDate || ! localToDate))
            {
                if (_relation == PriceType::EndDiscPurch ||
                    _relation == PriceType::EndDiscSales )
                {
                    // for end discounts, the QuantiyAmountField field contains order total amounts, not quantities
                    if (this.calcCur2CurPriceAmount(localQuantityAmountFrom, priceDiscTable) <= qty &&
                        ((qty < this.calcCur2CurPriceAmount(localQuantityAmountTo, priceDiscTable)) || !localQuantityAmountTo))
                    {
                        reselectBuffer();

                        discExist               = true;
                        discAmount             += this.calcCur2CurPriceAmount(priceDiscTable.Amount, priceDiscTable)/ this.priceUnit();
                        percent1               += priceDiscTable.Percent1;
                        percent2               += priceDiscTable.Percent2;
                        actualDiscTable        = priceDiscTable.data();
                        quantityAmount         += priceDiscTable.QuantityAmountFrom;
                    }
                }
                else
                {
                    if (localQuantityAmountFrom <= qty
                        && (qty < localQuantityAmountTo || !localQuantityAmountTo))
                    {
                        reselectBuffer();

                        discExist               = true;
                        discAmount             += this.calcCur2CurPriceAmount(priceDiscTable.Amount, priceDiscTable)/ this.priceUnit();
                        percent1               += priceDiscTable.Percent1;
                        percent2               += priceDiscTable.Percent2;
                        actualDiscTable        =  priceDiscTable.data();
                        quantityAmount         += priceDiscTable.QuantityAmountFrom;

                        this.mcrPriceDiscTableFound(priceDiscTable);
                    }
                    else
                    {
                        // If quantity does not qualify, but calculation potential then add as found
                        if (this.parmMCRPriceHistoryPotentialCalc())
                        {
                            reselectBuffer();

                            this.mcrPriceDiscTableFound(priceDiscTable);
                        }
                    }
                }
            }
        }

        // <GEERU>
        if (countryRegion_RU)
        {
            if (_accountCode == TableGroupAll::Table &&
                _agreementHeaderExtRecId &&
                _agreementPartnerCode)
            {
                accountRelation = _agreementPartnerCode;
            }
        }
        // </GEERU>

        if (!_inventDimId)
        {
            return false;
        }

        // To avoid flooding the cache the most granualated setup isn't cached.
        cacheMode = (_itemCode       != TableGroupAll::Table
                     || _accountCode != TableGroupAll::Table)
                    && !this.parmMCRPriceHistoryPotentialCalc();

        cacheValue = conNull();

        if (cacheMode)
        {
            key = this.makeKey(_relation,
                               _itemCode,
                               _itemRel,
                               _accountCode,
                               // <GEERU>
                               accountRelation,
                               // </GEERU>
                               _unitID,
                               _currency,
                               _inventDimId
                               // <GEERU>
                               ,_agreementHeaderExtRecId
                               // </GEERU>
                               );

            cacheValue = PriceDisc::getPriceDiscCacheValue(#cacheScope_FindDisc, key);
        }
        qty        = abs(_quantityAmount);

        if (cacheValue == conNull())
        {
            if (_itemCode != TableGroupAll::Table)
            {
                _unitID     = '';
            }

            while select priceDiscTable
                order by QuantityAmountFrom, FromDate
                where priceDiscTable.Relation            == _relation
                    && priceDiscTable.ItemCode           == _itemCode
                    && priceDiscTable.ItemRelation       == _itemRel
                    && priceDiscTable.AccountCode        == _accountCode
                    // <GEERU>
                    && priceDiscTable.AccountRelation    == accountRelation
                    // </GEERU>
                    && priceDiscTable.UnitId             == _unitID
                    && (priceDiscTable.Currency          == _currency
                    || (priceDiscTable.GenericCurrency
                    &&  priceDiscTable.Currency          == genericCurrency))
                    // <GEERU>
                    && (!countryRegion_RU || priceDiscTable.AgreementHeaderExt_RU == _agreementHeaderExtRecId)
                    // </GEERU>
                    && (priceDiscTable.InventDimId       == _inventDimId || this.parmMCRPriceHistoryPotentialCalc())
            {
                if (cacheMode)
                {
                    cacheValue += [[priceDiscTable.FromDate, priceDiscTable.ToDate, priceDiscTable.QuantityAmountFrom, priceDiscTable.QuantityAmountTo, priceDiscTable.RecId]];
                }
                else
                {
                    localFromDate            = priceDiscTable.FromDate;
                    localToDate              = priceDiscTable.ToDate;
                    localQuantityAmountFrom  = priceDiscTable.QuantityAmountFrom;
                    localQuantityAmountTo    = priceDiscTable.QuantityAmountTo;
                    localRecid               = priceDiscTable.RecId;

                    findDisc();

                    if (discExist
                        && !priceDiscTable.SearchAgain)
                    {
                        searchAgain = false;
                        break;
                    }
                }
            }
            if (cacheMode)
            {
                //We also want to cache the absence of discounts.
                if (cacheValue == conNull())
                {
                    cacheValue = [[0]];
                }

                PriceDisc::insertPriceDiscCache(#cacheScope_FindDisc, key, cacheValue);
            }
        }

        if (cacheMode
            && cacheValue)
        {
            discExist = false;
            for (i=1;i<=conLen(cacheValue);i++)
            {
                [localFromDate, localToDate, localQuantityAmountFrom, localQuantityAmountTo, localRecid] = conPeek(cacheValue, i);

                if (localRecid)
                {
                    findDisc();

                    if (discExist
                        && !priceDiscTable.SearchAgain
                        && !this.parmMCRPriceHistoryPotentialCalc())
                    {
                        searchAgain = false;
                        break;
                    }
                }
            }
        }
        return discExist;
    }
__________________
Facebook, mazzy.priot, mazzy.music.

Последний раз редактировалось mazzy; 14.03.2017 в 23:12.
Старый 14.03.2017, 23:16   #48  
Андре is offline
Андре
Moderator
Сотрудники компании GMCS
 
2,371 / 444 (19) +++++++
Регистрация: 03.12.2001
Цитата:
кроме того, что это фишка аксапты, если я правильно понимаю.
Дефолтные параметры ? Да они даже в C# есть

PHP код:
public void ExampleMethod(int requiredstring optionalstr "default string",
            
int optionalint 10
Цитата:
А вот тут похоже и есть корень.
В аксапте чаще наоборот. И об этом писал fed выше.

Мой код использует что-то из стандартного функционала аксапты.
как правило, я не до конца понимаю как работает стандарт на всех граничных значениях.
как правило, я не понимаю нафига сделано именно так.
Нет, мне кажется мы по разному понимаем unit тестирование. И мне кажется что мое понимание ближе к "каноническому"

Тест который тестирует твой код, и тест который тестирует стандартный функционал, который использует твой код - это разные тесты.

В тесте, который тестирует твой код - не надо проверять стандартный функционал. Может быть его вообще лучше замокать. Для проверки стандартного функционала будут свои unit тесты.

Каждый unit тест не проверяет весь мир вокруг. Он проверяет изолированный кусочек кода.

Код настолько легко будет тестировать с помощью unit-тестирования, насколько легко из него можно будет вычленять вот эти независимые кусочки. Код, который ты привел, с точки зрения тестирования - жесть. Его невозможно рассматривать независимо от всего мира вокруг. Он работает напрямую с таблицами БД, он обращается к статическим методам других таблиц. В него встроена (а не передается снаружи) обвязка для кеширования уже посчитанных значений. Это означает, что я не могу передать для нее mock. А надо. Так как это кеширование должно проверяться отдельно от этого метода, в отдельном unit тесте. А здесь я хочу тестировать логику метода.

У кода есть разные характиристики. Например, его производительность (насколько быстро он работает), читаемость (насколько легко читать код), поддерживаемость (насолько легко добавлять в код новые возможности). А еще есть такая характеристика, как тестируемость. Насколько легко код тестировать. Как и во всех остальных случаях, тестируемость кода не возникает сама по себе. Для этого надо прилагать дополнительные усилия. Иногда тестируемость может вхождить в конфликт с другими требованиями к коду. Например, с производительностью или даже читаемостью.

Очевидно, что у разработчиков Ax тестируемость кода не была приоритетом номер один.Скорее всего вообще, она не была в приоритетах. Я не говорю, что это плохо для продукта в общем. Наверное, расставив приоритеты таким образом они что-то и выиграли. Но, на мой взгляд, этот выбор привел к тому, что Ax и unit тестирование не рассматривают вместе.

Последний раз редактировалось Андре; 14.03.2017 в 23:35.
Старый 14.03.2017, 23:29   #49  
mazzy is offline
mazzy
Administrator
Аватар для mazzy
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
Most Valuable Professional
 
20,274 / 3072 (143) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Цитата:
Сообщение от Андре Посмотреть сообщение
Дефолтные параметры ? Да они даже в C# есть
они там появились совсем недавно.
в принципе народ пока только осваивает их.
раньше в C# нужно было делать кучу overload методов.
для каждого метода свой unit-тест и вася-кот. все было логично.

дефолтные параметры есть в basic, php.
но в этих системах не было слоев, из-за которых интерфейс был очень стабильным.

в общем, дефолтные параметры есть, конечно.
буду рад увидеть ссылки на рекомендации "как правильно" и для других систем )))

Цитата:
Сообщение от Андре Посмотреть сообщение
Нет, мне кажется мы по разному понимаем unit тестирование. И мне кажется что мое понимание ближе к "каноническому"

Тест который тестирует твой код, и тест который тестирует стандартный функционал, который использует твой код - это разные тесты.

В тесте, который тестирует твой код - не надо проверять стандартный функционал. Может быть его вообще лучше замокать. Для проверки стандартного функционала будут свои unit тесты.

Каждый unit тест не проверяет весь мир вокруг. Он проверяет изолированный кусочек кода.
возможно. надо подумать.

тут непонятно что делать, если свалился мой тест, но из-за того, что изменилось поведение в стандарте.
если отделять мух от котлет, то как отделить правильно?
__________________
Facebook, mazzy.priot, mazzy.music.
Старый 14.03.2017, 23:43   #50  
skuull is offline
skuull
Участник
Лучший по профессии 2014
Most Valuable Professional
 
252 / 185 (7) ++++++
Регистрация: 08.03.2013
Адрес: ХЗ
Цитата:
Сообщение от mazzy Посмотреть сообщение
как правило, я не понимаю нафига сделано именно так.

мало того, как правило, мой код всего лишь расширяет поведение какого-то стандартного метода.
А как мы можем протестировать то что не знаем как работат ? Методом черного ящика, а как тестируют этот ящик ? По спецификации. А где спецификация ? Нигде.
Ну значит надо покрыть тестами свои изменения, потому что мы знаем что мы делаем и забить на все остальное. Глядя на существующие юнит тесты в АХ многие из них написаны просто чтобы нагнать покрытие и получить волшебную зеленую лампочку, некоторые из них даже не асертят ничего и тут либо в МС сидят одни дураки либо это самый правильный подход с точки зрения отношения затрат к результату. Да и по моему скромному опыту и чекинам которые я видел, если АХ тесты падают, то проблема чаще в тесте чем в тестируемом методе...

Последний раз редактировалось skuull; 14.03.2017 в 23:45.
Старый 14.03.2017, 23:43   #51  
Андре is offline
Андре
Moderator
Сотрудники компании GMCS
 
2,371 / 444 (19) +++++++
Регистрация: 03.12.2001
Цитата:
тут непонятно что делать, если свалился мой тест, но из-за того, что изменилось поведение в стандарте.
если отделять мух от котлет, то как отделить правильно?
Сначала свалится тот тест, который тестирует поведеление стандартного функционала. Его поправят - раз поведение изменилось, надо и тест привести в соответствие.

Потом свалится твой тест. Это уже хорошо. Ради этого все и затевалось. То есть проблема не прошла незамеченной. И что делать дальше уже не так, по сути важно

Вроде только один вариант. Править свой код и свой тест, чтобы оно работало корректно с новой версией функционала. Особенно с учетом того, что править стандартный код становится все сложнее
Старый 15.03.2017, 00:36   #52  
Ruff is offline
Ruff
Дмитрий Ерин
Аватар для Ruff
1C
 
473 / 382 (13) ++++++
Регистрация: 18.09.2003
Адрес: Тула
Цитата:
Сообщение от mazzy Посмотреть сообщение
раньше в C# нужно было делать кучу overload методов.
Я почти уверен, что в Аксапте именно так и транслируются методы со значениями по умолчанию - в кучу overload методов. Если только в семерке в Х++ не добавили "именованные" параметры, как в шарпе, то получаем не 2^8 комбинаций дефолтности, а всего лишь 9.
Таким образом, можно свести дальнейший поиск стратегии к умозрительному варианту, что у нас в классе есть 9 методов findDisc:
X++:
boolean findDisc(PriceType             _relation, 
                  InventDimId           _inventDimId,
                  TableGroupAll         _itemCode, 
                  ItemId                _itemRel,
                  TableGroupAll         _accountCode,
                  CustVendAC            _accountRel,
                  UnitOfMeasureSymbol   _unitID,
                  Qty                   _quantityAmoun,
                  CurrencyCode          _currency,
                  AgreementHeaderExtRecId_RU _agreementHeaderExtRecId,
                  CustVendAC                 _agreementPartnerCode,
                  LogisticsPostalAddressRecId _deliveryPostalAddress)
{      
//  основной код 
}
// здесь еще 7 методов
// ...
boolean findDisc(PriceType _relation, InventDimId _inventDimId)  
{      
  return findDisc(_relation, _inventDim, 0,'',0,'','',0,CompanyInfo::standardCurrency(),0,'',0);     
}
...и дальше думать в этом направлении, поскольку:
Цитата:
Сообщение от mazzy Посмотреть сообщение
для каждого метода свой unit-тест и вася-кот. все было логично.
Старый 15.03.2017, 02:45   #53  
AlexSD is offline
AlexSD
Microsoft Dynamics
Сотрудники Microsoft Dynamics
 
211 / 188 (7) ++++++
Регистрация: 14.10.2003
Ой, что-то много понаписали. Не все прочитал. Напишу, как я бы действовал в таком случае.
1. Если я автор этого метода, то я бы написал ровно столько юнит-тестов, сколько сценариев его использования было бы мне известно на момент написания метода.
2. Если мне это метод достался мне в наследство, то написал бы столько юнит-тестов, что-бы покрыть весь код (100%)
3. Если мне пришла бага на фикс (или запрос на изменение) в этом методе, то написал бы ровно один юнит-тест, что бы проверить/покрыть только внесенные мной изменения.

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

Последний раз редактировалось AlexSD; 15.03.2017 в 02:47.
За это сообщение автора поблагодарили: mazzy (2).
Старый 15.03.2017, 10:41   #54  
fed is offline
fed
Участник
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
Ex AND Project
Соотечественники
 
2,019 / 3719 (128) ++++++++++
Регистрация: 13.03.2002
Адрес: İstanbul
Знаешь, я когда-то перевнедрял аксапту в одной фирме. И там местные финансисты заставили местных программистов в прошлой версии Аксапты переписать рассчет себестоимсти с ноля. Понятно что задача это была нелегкая - финансовый контролер не очень хорошо понимал аксапту, программист не очень хорошо понимал экономическую сущность задачи, поэтому финансовый контролер закономерно подозревал что себестоимость рассчитывается неверно. Поэтому он попросил другого программиста написать систему выверки для результатов работы расчета себестоимости. По факту - второй программист просто написал альтернативную систему рассчета себестоимости - в меру своего понимания объяснений финансового контроллера.
Время от времени, результаты выверки не сходились с результатами рассчета себестоимости. После этого финансист и оба разработчика садились и чего-то там где-то меняли (опять таки в меру своего понимания ситуации).
В итоге - когда я взялся за эту задачу (не было у меня шанса прогнуть клиента под стандартный функционал себестоимости), я обнаружил что в результате параллельной разработки, и себестоимости и выверка местами просто тупо имеют сходные баги и выдают одинаково неправильный результат.

К чему это я все написал:
Во первых - не употребляй, пожалуйста, иностранный термин "юнит-тест". Используй лучше хорошо знакомый тебе по годам автоматизации российской бухгалтерии термин выверка. Это сразу сделает тебе понятнее назначение этого процесса и поможет с формированием правильного к нему отношения.
Во вторых - надо очень четко понимать, что и юнит-тесты и выверка и предполетный досмотр в аэропорту - это все частные случаи того что в английском языке называется 'security theater'. Как известно, человеку с минимальной специфической подготовкой совсем не сложно обойти предполетный досмотр и пронести на борт что-то нехорошее. Поэтому реальная цель предполетного досмотра - это успокоить обычных пассажиров. Просто чтобы во время полета они не паниковали если их сосед арабской наружности встает с кресла, чтобы сходить в туалет. Нет, конечно от наиболее тупых террористов предполетный досмотр может помочь, но по факту - обычно наиболее тупые персонажи слишком бедны, чтобы подготовить столь сложный в организации террористический акт.
Вот и к выверке/юнит-тесту надо относиться точно также как к предполетному досмотру. От сложных ошибок они мало помогают, а тупые баги, скорее всего, будут достаточно очевидны и без юнит-тестов...
За это сообщение автора поблагодарили: mazzy (2).
Старый 15.03.2017, 10:58   #55  
mazzy is offline
mazzy
Administrator
Аватар для mazzy
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
Most Valuable Professional
 
20,274 / 3072 (143) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Цитата:
Сообщение от fed Посмотреть сообщение
Используй лучше хорошо знакомый тебе по годам автоматизации российской бухгалтерии термин выверка.
выверка - это пять!!!

правда выверка - это что-то постфактум происходящее.
так отрезается TDD всякое и прочие автоматизированные код-генераторы...

но выверка - это пять!!!
да, люди в основном говорят о регрессии. в этом смысле выверка - самый подходящий термин.
причем, выборочная выверка ))))
__________________
Facebook, mazzy.priot, mazzy.music.
Старый 15.03.2017, 11:07   #56  
belugin is offline
belugin
Участник
Аватар для belugin
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
Сотрудники Microsoft Dynamics
 
3,762 / 1855 (69) ++++++++
Регистрация: 16.01.2004
Адрес: Москва
Цитата:
Сообщение от fed Посмотреть сообщение
Вот и к выверке/юнит-тесту надо относиться точно также как к предполетному досмотру. От сложных ошибок они мало помогают, а тупые баги, скорее всего, будут достаточно очевидны и без юнит-тестов...
А почему только к юнит тесту? Мне кажется, тестирование вообще включая человеческое это выверка, следовательно, security theater согласно логике

Цитата:
От сложных ошибок они мало помогают, а тупые баги, скорее всего, будут достаточно очевидны и без юнит-тестов..
Угу только для этого надо сажать кого-то кто будет регулярно тестировать всю систему по всем веткам, еще желательно после каждого изменения .

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

А еще можно гонять их фонов прямо в IDE - см http://www.ncrunch.net/
Старый 15.03.2017, 11:12   #57  
belugin is offline
belugin
Участник
Аватар для belugin
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
Сотрудники Microsoft Dynamics
 
3,762 / 1855 (69) ++++++++
Регистрация: 16.01.2004
Адрес: Москва
А еще есть книжка Эффекстивная работа с унаследованным кодом там как раз про то, как писать тесты для говнокода.

Есть такое понятие pinning tests - когда надо на существующую систему написать тесты просто чтобы зафиксировать ее поведение при определенных параметрах. Я даже временные такие делал (когда, например, тестов нет, но надо ускорить какой-то код, но не сломать суенарий в целом) - помогает.
За это сообщение автора поблагодарили: mazzy (2).
Старый 15.03.2017, 11:33   #58  
VORP is offline
VORP
Участник
 
94 / 74 (3) ++++
Регистрация: 26.05.2006
Надо протестировать что каждый из параметров отрабатывает правильно, то есть его изменения приводят к ожидаемому результату, а потом написать интеграционный тест где задать все параметры, убедившись что они взаимодействуют правильно. Было на тренинге по Acceptance Tests.
На самом деле тут (я имею ввиду Аксапту вообще) проблема другая, а именно что одним из параметров является состояние системы. Положим, для простоты что считывается значение из LedgerParameters, а дальше исполнение идёт по разным веткам. Тогда получается что хоть явного параметра и нет, а на самом деле он есть.
Касательно метода PriceDisc.findPrice - ну это, как правильно замечено, один из важных методов системы, который считает цену. Для него 256 тестовых методов это в общем то может быть и нормально. Самое сложное, может быть, в данном случае это с помощью Reverse Engineering описать эти 256 сценариев.
Старый 15.03.2017, 11:45   #59  
mazzy is offline
mazzy
Administrator
Аватар для mazzy
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
Most Valuable Professional
 
20,274 / 3072 (143) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Цитата:
Сообщение от VORP Посмотреть сообщение
Надо протестировать что каждый из параметров отрабатывает правильно, то есть его изменения приводят к ожидаемому результату, а потом написать интеграционный тест где задать все параметры, убедившись что они взаимодействуют правильно.
да, в принципе согласен.
но. задать надо вручную? 256 комбинаций?
тот, кто проводит ревью теста тоже должен проверить вручную?

когда добавится еще один параметр (и он будет дефолтным),
то бедняга добавляющий должен будет добавить еще 256 строк?
а ревьюирующий должен будет проверить вручную?

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

Цитата:
Сообщение от VORP Посмотреть сообщение
На самом деле тут (я имею ввиду Аксапту вообще) проблема другая, а именно что одним из параметров является состояние системы. Положим, для простоты что считывается значение из LedgerParameters, а дальше исполнение идёт по разным веткам. Тогда получается что хоть явного параметра и нет, а на самом деле он есть.
да. но тут ситуация хоть как-то облегчается тем, что каждый запуск каждого тестового метода выполняется в одном и том же окружении.

Цитата:
Сообщение от VORP Посмотреть сообщение
Касательно метода PriceDisc.findPrice - ну это, как правильно замечено, один из важных методов системы, который считает цену. Для него 256 тестовых методов это в общем то может быть и нормально. Самое сложное, может быть, в данном случае это с помощью Reverse Engineering описать эти 256 сценариев.
в принципе да.
но я понимаю людей, которые отвечают на такой довод "здравый смысл" )))))
__________________
Facebook, mazzy.priot, mazzy.music.
Старый 15.03.2017, 12:13   #60  
VORP is offline
VORP
Участник
 
94 / 74 (3) ++++
Регистрация: 26.05.2006
но. задать надо вручную? 256 комбинаций? - Насчёт вручную не понял - но автоматизировать это мне кажется не получится если речь об этом. В моём понимании надо ревьюить не столько код теста, сколько описание сценариев, которое надо сделать до написание теста, иначе в таком случае недолго и запутаться.
Надо для каждого из параметров сформировать список "имеющих смысл" значений, например клиент который есть в PriceDiscTable, и которого нет или количество(Qty) больше порога или меньше, и на каждый написать тест.
Когда добавится новый параметр, надо опять же протестировать этот параметр с имеющими смысл значениями а также изменить интеграционный тест, как я понимаю. Ну то есть если бы добавили параметр количество(Qty которое уже есть) новых тестов было бы два + измененный интерграционный.

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

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

Последний раз редактировалось VORP; 15.03.2017 в 12:15.
Теги
unit test, как правильно, тестирование

 

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 03:15.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.