Как стать автором
Обновить

Комментарии 21

А вот фреймворк Moq (http://code.google.com/p/moq/) какие-то похожие возможности не предоставляет? Вроде, что-то общее в примерах проглядывается.
В природе существует предостаточное количество Isolation Frameworks с более-менее похожими возможностями, при этом каждый фреймворк имеет свои особенности. К примеру я не нашел в Moq возможности редиректить вызовы статических и приватных членов классов — а вот Moles умеет это делать.
В конце концов, каждый волен выбирать то, что ему подходит больше.
Ага, видимо как раз для статических и приватных и приходится генерировать отдельные классы.
Единственный Isolation Framework который умеет мокать статичные, невиртуальные и приватные методы — это Typemock Isolation. Но он весьма не дешевый — хотя и очень функциональный
Moles делает то же самое даром.
Moles is also available separately for commercial use without requiring an MSDN subscription.
НЛО прилетело и опубликовало эту надпись здесь
используя такой подход, вы завязываете реализацию своих тестов на внутренней структуре объектов, а не на интерфейсе объектов, иначе говоря, тесты будут ломаться, когда код будет фактически работоспособным.

Упавшие тесты не составит особого труда пофиксить, а что вам даст уверенность что после изменения внутренней структуры класса, вы не поломали его логику?
Я для того и упомянул об особенностях юнит тестирования в обзоре, — обратите внимание на пункт Для чего нужно юнит тестирование.
При адекватном использовании моков, такие неоправданные падения тестов будут скорее исключением чем правилом.
Во вторых, упавшие тесты заставляют держать руку на пульсе — человек который их фиксит, волей-неволей должен вникнуть в логику работы класса который он изменил(а юнит тесты как раз и документируют логику классов). Это особенно важно в коллективной разработке, когда над одним проектом трудятся десяток и более девелоперов.

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

Вам не кажется что это те же самые моки, только реализованные вручную? Какие преимущества с точки зрения юнит-тестирования дает такой подход(кроме увеличения скорости работы тестов и простоты набивки тестовых данных)?
НЛО прилетело и опубликовало эту надпись здесь
«Проверяй результат, не проверяй реализацию»

приведу цитату из очень хорошей статьи:
Unit-тестирование условно делится на два подхода:
* state-based testing, в котором мы тестируем состояние объекта после прохождения unit-теста
* interaction (behavioral) testing, в котором мы тестируем взаимодействие между объектами, поведение тестируемого метода, последовательность вызовов методов и их параметры и т.д.
… Иногда просто удобнее проверить состояние объекта, а иногда – его взаимодействие с другими объектами. Поэтому эти два подхода прекрасно уживаются вместе, когда вы понимаете, о чем идет речь, и что именно вы хотите сейчас проверить.


НЛО прилетело и опубликовало эту надпись здесь
behavioral testing это не от хорошей жизни

по сути, само юнит-тестирование не от хорошей жизни… а behavioral testing это всего лишь один из подходов, ни больше ни меньше. А говорить что какой то из подходов хуже/лучше бессмысленно, нужно просто правильно их использовать, и да будет счастье.
Оно, конечно, круто и мощно. Но ведь для тестирования 15-строчного кода понадобилось написать 55 строк теста. Не многовато ли?
Рефакторинг тестов никто не отменял. Учитывая что тестовых ситуаций могут быть десятки, можно инициализацию тестовой среды вынести в отдельные методы и использовать их повторно с передачей нужных параметров.
А если тестовая ситуация одна, то без использования моков вам понадобится создавать свои реализации fake обьектов, или обращаться к реальным обьектам, перед забив их тестовыми данными — на это тоже потребуется немало усилий по реализации и сопровождению. Плюс вы таким образом уходите в сторону от концепции юнит тестирования.
Интересно. Сам использую Typemock, но надо будет глянуть на всякие там Pex, Chess, Moles.
Спасибо за обзор. Первое впечатление о Moles — громоздко и многословно. Но, если разобраться и не злоупотреблять, т. е. не добавлять лишней логики и ограничиться возвращением заранее подготовленных значений, то получается все довольно аккуратно. Почти как в Moq.
что за параметр dataMapper — инстанс класса FileManager?

PS. Вообще, спасибо. Познавательно.
Извиняюсь, — исправил на fileManager. Забыл переименовать переменную после переименования класса с FileDataMapper на FileManager.
"# Реализация теста должна быть максимально простой и не требовать инициализации тестового окружения вне теста(например подготовки тестовых данных в БД).
[...]
# Прохождение теста не должно зависеть от внешних систем(сервера баз данных, веб-сервера, файловая система, контроллеры внешних устройств), если он не предназначен для тестирования взаимодействия с этими системами."
Одно противоречит другому. Таки если я написал юнит-тест для тестирования взаимодействия с этой системой, как мне ее оттестировать, не инициализируя эту систему?

Ну и да. Идея о том, что юнит-тест должен работать без тестового окружения (например, БД) приводит к тому, что нам приходится писать два разных теста — сначала юнит-тест, который проверит, что на моке все работает, а потом юнит-тест, который проверит, что БД работает так же, как мок. А вы пробовали когда-нибудь найти все особенности поведения крупного DAL? Когда на моке, который возвращает IQueryable, собранный из массива данных в памяти, все работает (потому что работает LINQ to objects), а на боевому запуске, где IQueryable из датаридера, все падает (потому что работает, скажем, LINQ to SQL, в котором нет поддержки нужного метода).
# Прохождение теста не должно зависеть от внешних систем(сервера баз данных, веб-сервера, файловая система, контроллеры внешних устройств), если он не предназначен для тестирования взаимодействия с этими системами."
Одно противоречит другому. Таки если я написал юнит-тест для тестирования взаимодействия с этой системой, как мне ее оттестировать, не инициализируя эту систему?

Если вы написали автоматический тест для тестирования взаимодействия вашего модуля со сторонней системой, то это уже не юнит-тест а integration test. Само по себе понятие «юнит-тест» предполагает тестирование логики реализованной в вашем модуле(группе модулей).
Ну и да. Идея о том, что юнит-тест должен работать без тестового окружения (например, БД) приводит к тому, что нам приходится писать два разных теста — сначала юнит-тест, который проверит, что на моке все работает, а потом юнит-тест, который проверит, что БД работает так же, как мок. А вы пробовали когда-нибудь найти все особенности поведения крупного DAL? Когда на моке, который возвращает IQueryable, собранный из массива данных в памяти, все работает (потому что работает LINQ to objects), а на боевому запуске, где IQueryable из датаридера, все падает (потому что работает, скажем, LINQ to SQL, в котором нет поддержки нужного метода).

Правильный подход в данном случае — написание тестов для:
1 — тестирования логики которая формирует IQueryable на соотвествие формирования запросов.
2 — тестирования логики LINQ to SQL на наличие поддержки необходимых методов.
Для большого проекта вторую группу тестов вам понадобится реализовать всего один раз.

«Правильный подход в данном случае — написание тестов для:
1 — тестирования логики которая формирует IQueryable на соотвествие формирования запросов.
2 — тестирования логики LINQ to SQL на наличие поддержки необходимых методов.»
Вы самолично пробовали это сделать? Хотя бы раз?

Вообще, сама идея, что мне надо писать юнит-тесты на Linq2Sql, меня очень веселит.

Про что, собственно, и речь — что идея «давайте напишем изолированный тест с моками» приводит к тому, что сложность написания мока превосходит сложность тестируемого кода. Честное слово, инициализировать базу, учитывая наличие соответствующих систем, иногда проще.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории