Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Какие объекты имитировать при выполнении TDD

Автор вопроса: Jamie DixonОтветов: 10

При создании методов следует ли передавать каждый объект, созданный внутри этого метода, в качестве параметра, чтобы эти объекты можно было имитировать в наших модульных тестах?

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

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

Что вы думаете? Должны ли все объекты, созданные внутри метода, передаваться в качестве параметров?

  • каждый объект, созданный внутри этого метода, будет передан. Возможно, вы захотите перефразировать это. Это сбивает с толку. Я думаю, вы спрашиваете, должны ли они быть переданы вместо создания экземпляра внутри метода. Будет немного больше смысла, если это будет считаться альтернативой. S.Lott 02.11.2009

unit-testingmockingtdd

Ответы

Ответ 1 · рейтинг 7 · Mark Seemann

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

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

Допустим, ваша исходная реализация выглядела так:

public class Foo
{
    public Ploeh DoStuff(Fnaah f)
    {
        var bar = new Bar();
        return bar.DoIt(f);
    }
}

Это можно изменить, чтобы оно выглядело так:

public class Foo
{
    private readonly IBar bar;

    public Foo(IBar bar)
    {
        this.bar = bar;
    }

    public Ploeh DoStuff(Fnaah f)
    {
        return this.bar.DoIt(f);
    }
}

Обратите внимание, что я изменил bar с экземпляра Bar на экземпляр IBar, тем самым отделив Foo от конкретной реализации IBar. Такой рефакторинг, как правило, упрощает написание ваших модульных тестов и поддержку, поскольку теперь вы можете независимо изменять реализации Foo и Bar.

Ответ 2 · рейтинг 6 · Philip Rieck

Первая часть - немного нагруженный вопрос. Это все равно, что спросить: "Когда я наезжаю на пешеходов, должен ли я держать руль обеими руками?"

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

Однако один из ключевых способов сделать ваш код пригодным для тестирования - использовать IoC (инверсия управления), когда ему передаются зависимости класса (или метода), а не класс, запрашивающий их. Это значительно упрощает тестирование, так как вы можете передавать моки.

Итак, короткий ответ: "Да", передайте свои зависимости и посмотрите на хороший внедрение зависимостей составная часть. Длинный ответ: "Да, но не делайте этого". Фреймворки DI, вероятно, заставят вас передавать зависимости объектам, а не методам, и вы обнаружите, что хотите ограничить эти зависимости, что хорошо.

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

Ответ 3 · рейтинг 2 · Stefan Steinegger

Просто наблюдение: вы говорите о методах, а я предпочитаю говорить о классах.

На этот вопрос нет общего ответа. Иногда очень важно отделить создание класса от его использования.

Рассмотреть возможность:

  • если класс использует другой, но вы хотите их слабо связать, вам следует использовать интерфейс. Теперь, если известен только интерфейс, а не конкретный тип, как первый класс должен создать экземпляр?
  • разделение классов очень важно, но не может и не должно быть сделано в каждом случае. Если есть сомнения, если следует разъединить.
  • При использовании внедрения необходимо решить, кто создает и внедряет экземпляры. Скорее всего, вам пригодится инфраструктура внедрения зависимостей, такая как Spring.Net.

Есть еще одна хитрость: сделайте впрыск необязательным. Это означает, что когда вы передаете экземпляр в конструктор, он будет использоваться. Если нет, новый экземпляр создается самим классом. Это удобно при работе с устаревшим кодом и при отсутствии инфраструктуры внедрения зависимостей.

Ответ 4 · рейтинг 2 · Dror Helper

[Отказ от ответственности: я работаю в Typemock]
У вас есть три варианта, два из которых требуют некоторого рефакторинга:

  1. Передайте все аргументы - как вы уже знаете, таким образом вы можете передавать макеты/заглушки вместо "настоящих" объектов.
  2. Используйте контейнер IoC - рефакторинг вашего кода, чтобы использовать контейнер для захвата объектов из вашего кода, и вы можете заменить их макетами/заглушками.
  3. Используйте Typemock Isolator (.NET), который может подделывать будущие объекты - объекты, экземпляры которых создаются внутри вашего кода из тестового кода. . Этот вариант не требует рефакторинга, и если у вас большая база кода, он должен стоить своего кода.

Дизайн для тестируемости не всегда является хорошей практикой, особенно с существующим проектом, в котором вы уже написали некоторый код. Поэтому, если вы начинаете с чистого листа или у вас небольшой проект, возможно, можно передавать объекты в качестве аргументов конструктору класса, если у вас не слишком много параметров.
- Если вы используете контейнер IoC.

Если вы не хотите менять весь свой существующий код и/или не хотите проектировать свой код определенным образом, чтобы сделать его "более пригодным для тестирования" (может привести к странному виду кода - используйте Isolator (или аналогичный инструмент, если вы повторно использовать Java).

Ответ 5 · рейтинг 2 · philant

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

Используйте mock, когда хотите изолировать класс от других. Используйте mock, поэтому держите тесты подальше от

  • файловые системы
  • базы данных
  • сети
  • объект с непредсказуемым поведением (часы, генератор случайных чисел...)

Отделить использование объекта от его конструкции

Ответ 6 · рейтинг 1 · Johnno Nolan

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

Там, где у вас много зависимостей, вы можете найти контейнер IoC, который поможет вам управлять ими всеми.

При работе с устаревшим кодом полезно прочитать статью Майкла Фезера Эффективная работа с Устаревший код. В книге есть много способов, как протестировать вашу систему.

Ответ 7 · Mike Trpcic

Я не уверен, какой язык/инструменты вы используете, но способ, которым это можно сделать, это просто издеваться над конструктором, например:

@user = mock_model(User)
User.stub(:create).and_return(@user)

Так что с этого момента в ваших тестах, если вы вызываете User.create (User - это "Класс"), он всегда будет возвращать вашу предопределенную переменную @user, позволяя вам полностью контролировать, какие данные используются в ваших модульных тестах.

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

Ответ 8 · FinnNk

Вы должны отдавать предпочтение методам, которые не принимают аргументов, за которыми следует один аргумент, два и, наконец, три. Все, что занимает больше 3, является запахом кода. Либо есть класс, ожидающий обнаружения во всех передаваемых аргументах, либо класс/метод пытается сделать слишком много.

Что касается передачи зависимостей, вы можете использовать внедрение конструктора, но со временем это становится громоздким, поскольку вы постепенно переходите к необходимости передачи всего графа объектов. Я бы посоветовал как можно раньше перейти к использованию контейнера IoC и избежать боли.

Ответ 9 · Frank Schwieterman

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

Ответ 10 · kyoryu

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

Чтобы сделать это эффективно, требуется, чтобы ваши интерфейсы были на семантическом уровне, а не на уровне реализации в целом.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы