Чисто в теории, наверное надо использовать mock объекты, в частности для CustshipmentsAccessor. А дальше писать тест-кейс, в котором изобразить что-то вроде:
EXPECT_CALL( SetShipmentPricesForCustomer )
Но это в теории все прекрасно, а как написать нормальные тесты с mock объектами — я не знаю. Хотя опять же в теории тест-кейсы с мок-объекатми должны также прекрасно документировать код. Типа посмотрел на кейс и сразу виден весь алгоритм. Но это видимо сродни искусству....
Оставлю свое мнение по вопросу, вдруг кому-то пригодится.
Первое что хочу сказать: не стоит пытаться обезопасится от таких ошибок с помощью тестирования. Придется написать тонны тестов, из которых хорошо если один стрельнет (а как извесно, хороший тест -- это тест который повалился).
Что же можно сделать чтобы уберечь себя от подобных ошибок? ИМХО, проектировать удобный "error free" интерфейс.
У меня есть 3 предложения по улучшению этого интерфейса:
1) ForAllCustomers и ForCustomer вынести в начало названия.
Получим ForAllCustomersSetShipmentPrices и ForCustomerSetShipmentPrices. Ошибиться стало намного сложнее.
Для пущего удобства чтения можно добваить подчеркивание: ForAllCustomers_SetShipmentPrices и ForCustomer_SetShipmentPrices (и шлите в лес всех кто говорит, что подчеркивчания "некошерны". ИМХО, кошерно все что удобно. Тут подчеркивание удобно).
Как бонус получаем разделение методов на две группы в автодополнении. (это, кстати, наведет вас на мысль, что класс играет две роли: работа с одним кастомером и со всем кастомерам, а оттуда недалеко до двух классов)
Не совсем понимаю как получается, что SetShipmentPricesForCustomer не получает параметром конкретного кастомера. Возможно, он передается из CustomerAccessor через какой-то контекст.
2) Можно передавать кастомера параметром в метод. Тогда SetShipmentPricesForAllCustomers банально не скомпилируется.
3) Можно проверять наличие кастомера в контексте из SetShipmentPricesForAllCustomers. И если он там есть выкидывать исключение (с пояснением что как и почему).
В общем нужно проектировать удобные интерфейсы -- те, которые кроме решения своих непосредственных задач не позволяют использоать себя неправильно.
Здравствуйте, Aikin, Вы писали:
A>1) ForAllCustomers и ForCustomer вынести в начало названия.
Я уже типа того и сделал, только еще больше переименовал
A>Не совсем понимаю как получается, что SetShipmentPricesForCustomer не получает параметром конкретного кастомера. Возможно, он передается из CustomerAccessor через какой-то контекст. A>2) Можно передавать кастомера параметром в метод. Тогда SetShipmentPricesForAllCustomers банально не скомпилируется.
Одна функция принимает первым параметром int customerID, а другая int countryID =) Поэтому так и вышло
Re[3]: Как поймать тестами такой баг?
От:
Аноним
Дата:
27.05.10 16:57
Оценка:
Здравствуйте, MozgC, Вы писали:
A>>2) Можно передавать кастомера параметром в метод. Тогда SetShipmentPricesForAllCustomers банально не скомпилируется. MC>Одна функция принимает первым параметром int customerID, а другая int countryID =) Поэтому так и вышло
Недавно, кстати, AndrewK писал о типизированных Id.
Т.е. вместо int CountryId пишем Key<Country>, мне понравилось, начал использовать у себя
Здравствуйте, MozgC, Вы писали:
A>>Не совсем понимаю как получается, что SetShipmentPricesForCustomer не получает параметром конкретного кастомера. Возможно, он передается из CustomerAccessor через какой-то контекст. A>>2) Можно передавать кастомера параметром в метод. Тогда SetShipmentPricesForAllCustomers банально не скомпилируется. MC>Одна функция принимает первым параметром int customerID, а другая int countryID =) Поэтому так и вышло
Теперь понятно. Опять проблема интерфейса.
Надо узнать что же предлагал AndrewK, а то у меня кроме экстеншен метода для int (id.of<Customer>()) и new Customer(id) ничего в голову не приходит.
Здравствуйте, <Аноним>, Вы писали:
А>Недавно, кстати, AndrewK писал о типизированных Id. А>Т.е. вместо int CountryId пишем Key<Country>, мне понравилось, начал использовать у себя
А можно пример? А еще лучше ссылку?
Здравствуйте, Aikin, Вы писали:
А>>Недавно, кстати, AndrewK писал о типизированных Id. А>>Т.е. вместо int CountryId пишем Key<Country>, мне понравилось, начал использовать у себя A>А можно пример? А еще лучше ссылку?
Угу, об этом я знаю. Еще МакКоннел в совершенном коде писал. Вот только совсем не затронут более важный вопрос: как удобно создать этот ключ.
Мест где нужно передать ключ намного больше чем мест где ключ объявлен. Поэтому нужно думать о юзабилити создания ключа из int'а, а не о том как же удобно описать сам ключ.
Я до сих пор использовал new Customer(id) в случаях когда нужна типизация.
Здравствуйте, MozgC, Вы писали:
MC>Здравствуйте, fk0, Вы писали:
fk0>> НАДО ДАВАТЬ ФУНКЦИЯМ НОРМАЛЬНЫЕ ИМЕНА. ЧИИИИТААААЕЕЕМЫЫЫЫЕЕЕЕЕ! А не блаблабла в пол-экрана шириной.
MC>Понимаете, в чем дело, щас не старинные экраны в которые по 60 символов влезает, а широкоформатные мониторы, к тому же современные IDE избавляют от необходимости полностью имена классов и методов печатать. Зато если я сам посмотрю на свой код через год — у меня не возникнет вопросов "а что делает этот метод?" — это ясно из названия. То же самое если в проект придет новый человек — ему будет проще понять что делает конкретный класс или метод просто по одному названию.
Здравствуйте, MozgC, Вы писали:
MC>Здравствуйте, fk0, Вы писали:
fk0>> НАДО ДАВАТЬ ФУНКЦИЯМ НОРМАЛЬНЫЕ ИМЕНА. ЧИИИИТААААЕЕЕМЫЫЫЫЕЕЕЕЕ! А не блаблабла в пол-экрана шириной.
MC>Понимаете, в чем дело, щас не старинные экраны в которые по 60 символов влезает, а широкоформатные мониторы, к тому же современные IDE избавляют от необходимости полностью имена классов и методов печатать. Зато если я сам посмотрю на свой код через год — у меня не возникнет вопросов "а что делает этот метод?" — это ясно из названия. То же самое если в проект придет новый человек — ему будет проще понять что делает конкретный класс или метод просто по одному названию.
И что, теперь моск тоже со скоростью видеокарты работает
От перепутывания и опечаток не спасут и короткие имена.
Попробуйте не перепутать i и j в каком-нибудь алгоритме сортировки.
Здравствуйте, MozgC, Вы писали:
MC>Здравствуйте, fk0, Вы писали: fk0>>Не, я конечно тоже против 6-буквенных имён, но есть две крайности... MC>Т.е. по вашему SetCustomerShipmentPrices() — это крайность?
А ты как сам печатаешь подобные названия? Вручную или через автокомплит? ИМХО длинность названия как раз и провоцирует пользоваться автокомплитом, а он как раз создает благодатную почву для таких ошибок.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>А ты как сам печатаешь подобные названия? Вручную или через автокомплит? ИМХО длинность названия как раз и провоцирует пользоваться автокомплитом, а он как раз создает благодатную почву для таких ошибок.
код чаще читают, чем пишут
Стив МакКонел. Совершенный код
Во-вторых, здесь неверно выбрано название функции, вернее порядок слов в ней. Что уже неоднократно обсуждалось.
MC>Но вопрос в том — как лучше тестировать чтобы такая ошибка обнаружилась. Ведь если для каждой функции писать кучу тестов и код, который проверяет как изменилось состояние БД (не затронулись ли другие строки и таблицы), то с ума можно сойти (я все-таки не маньяк-тестировщик, тесты в основном пишу только на наиболее критичные для бизнеса, либо сложные функции). Можно ли как-то проще?
первое что пришло на ум
1) Ослабить связь между слоями логики и доступа к данным, так чтобы ко всем XXXAccessor можно было MOK-объекты сделать. (можно и на слой ниже)
2) дальше само собой получится, что CustshipmentsAccessor.SetShipmentPricesForCustomer будет ожидаемым вызовом в тесте
3) НО это не спасет от случая когда 2 раза и в коде и в тесте будет вызов не того метода.
Для себя выработал правило: не должно быть в интерфейсе имен вида CommonName
CommonNameExtra
Допустимы только непустые суффиксы: CommonNameOne
CommonNameTwo