Unit тестирование в Android c Clean architecture

แชร์
ฝัง
  • เผยแพร่เมื่อ 17 พ.ย. 2024

ความคิดเห็น • 45

  • @TimofeyKovalenko
    @TimofeyKovalenko  2 ปีที่แล้ว +9

    СОДЕРЖАНИЕ:
    00:00:00 - введение
    00:00:39 - Clean architecture Android
    00:01:40 - кратко, что такое Unit тестирование Android
    00:02:25 - подключаем библиотеки JUnit и Mockito
    00:03:26 - пишем тест на JUnit 5
    00:14:46 - используем библиотеку Mockito

  • @MarselNz
    @MarselNz 2 ปีที่แล้ว +9

    Единственный человек во всем ютубе, кто дает такую актуальную информацию. Спасибо вам

  • @dmitriygushel5587
    @dmitriygushel5587 2 ปีที่แล้ว +7

    Спасибо, очень подробно! Кстати, что бы не создавать самому пакеты, можно нажать ctrl+shift+T и студия сама создаст класс с названием исходного класса+test с тем же пакетом в папке test

  • @readmeandanswer8142
    @readmeandanswer8142 2 ปีที่แล้ว +5

    Спасибо, понятно, после ваших уроков статьи по этим темам читаются легче.

  • @chriswaytt6009
    @chriswaytt6009 ปีที่แล้ว +2

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

  • @spyro2008
    @spyro2008 7 หลายเดือนก่อน

    Как всегда все на высшем уровне, спасибо вам!

  • @Arman_127
    @Arman_127 2 ปีที่แล้ว +1

    Выглядит и вправду смешно но в реальных проектах я думаю пойму всю прелесть использование тестов спасибо

  • @alexanderj8981
    @alexanderj8981 2 ปีที่แล้ว +8

    Спасибо за видео. Понравился стиль объяснения, спокойный рассказ с примерами без лишней воды - приятно слушать такое.
    Я конечно понимаю, что все упрощено для новичков, но некоторые моменты хотелось бы уточнить, т.к. где-то можно чуть улучшить, а где-то из-за упрощения даются немного вредные советы, которые сказываются потом.
    1. th-cam.com/video/iGqVm0atAMQ/w-d-xo.html : вообще вначале, когда проект почти пустой и все элементарно, то классы с тестами могут лежать в точно таких же пакетах и называться по имени класса. Но чем дальше, тем все больше тесты должны быть структурированы иначе, чем основной код. У них будет единый фасад как точка входа, но вовсе не обязательно, что на каждый класс вообще будет ровно симметричный класс с тестами. Тесты должны быть не чувствительными к изменению структуры основного кода, а это означает, что если вдруг из того же UseCase мы захотим вынести что-то в отдельный класс или сделать изменение, которое не меняет поведение программы, то тесты при этом должны практически не меняться и на новый выделенный класс не создаются новые тесты, ведь поведение уже будет протестировано через внешний.
    Если соблюдать это правило про класс и симметричный тест класс к нему под тем же именем и расположением, то мы создаем лишнюю связанность, которая будет мешать при дальнейших изменениях кода.
    2. th-cam.com/video/iGqVm0atAMQ/w-d-xo.html : еще хорошо бы показать, раз код в IDE, что тесты создавать проще не вручную, а через хоткеи: Alt+Enter (если windows) и там через диалог дальше. А создавать тесты в том же пакете и переключаться между основным кодом и тестами можно по Ctrl+Shift+T. Вот тут можно посмотреть справку: www.jetbrains.com/help/idea/create-tests.html.
    3. th-cam.com/video/iGqVm0atAMQ/w-d-xo.html : стандартным Assertions лучше предпочесть AssertJ и ему подобные с более выразительным синтаксисом, где вместо assertEquals(a, b), в котором новички долго путаются где “ожидаемое”, а где “реальное” на каком месте, - там будет assertThat(2+2).isEqual(4).
    4. th-cam.com/video/iGqVm0atAMQ/w-d-xo.html : вообще-то, названия тестов как предложения с пробелами в котлине работают и для junit4, тут никакой разницы. “shouldReturnCorrectData” - совсем слабое название и второй вариант заметно лучше. И хотя “should” достаточно распространенная практика именования, но следовать ей не стоит: это лишнее слово в каждом тесте, притом еще и лексически означающее, что “тут следовало бы вернуть (но не обязательно)”, а мы хотим, чтоб поведение было строгим точным требованием без “желательно”.
    5. th-cam.com/video/iGqVm0atAMQ/w-d-xo.html : а вот это неправильный и вредный совет. Как раз любым mock библиотекам лучше предпочитать свои тестовые утилиты с настоящими и полноценными методами, а где можно, то и реальные классы. Для этого нужно аккуратно продумать куда положить такой TestRepository, чтоб можно было переиспользовать в других тестах (т.е. не тут в этом же файле). Для этого кстати нельзя просто так забивать имена внутри (first и last name), а придется передавать данные параметрами. Это делается очень просто и потом легко менять в одном месте раз и для всех тестов. А вот с моками если что меняется, то нужно обновить каждый тест в отдельности. Настройка каждого мока - кроме того, что это лишний код - так это и каждый раз излишняя привязка к тому, как тест ожидает какой метод вызовется в основном коде и как он себя ведет. А это означает, что тест знает детали имплементации, чего он знать не должен. И именно это потом не позволяет менять основной код без тонны изменений в тестах.
    6. th-cam.com/video/iGqVm0atAMQ/w-d-xo.html : mockito был стандартом в java. С котлином он изначально плохо работал. Зато упомянутый mockk и советую использовать вместо mockito - он создавался сразу для котлина с его синтаксическими возможностями, не требует второй вспомогательной библиотеки, более мощный (может при необходимости и object, и enum, и конструкторы чужих классов мокать).
    7. th-cam.com/video/iGqVm0atAMQ/w-d-xo.html : вообще да, про это и вправду надо задумываться будет ли сравнение по ссылке или по equals тоже сработает. Но для теста самого это не должно быть важным, ведь поведение будет работать так или иначе. Даже технически именно сам testUserName, а не копию, метод и вернет. А если очень важно убедиться, что возвращенное значение equals подобному, то это отдельно можно протестировать. Есть в AssertJ и методы такие как assertThat(actual).isSameAs(expected) (или isNotSameAs).

    • @TimofeyKovalenko
      @TimofeyKovalenko  2 ปีที่แล้ว +3

      Уххххх) Спасибо за такой развернутый фидбек 👍.
      1) Не могу полностью согласиться. Да, когда проект большой, то и тестов бывает много на один класс, а где-то и структура другая. Но для большинства классов классическая структура более чем подходит, и позволяет проще ориентироваться в коде, особенно, когда заводишь в проект новых разработчиков. К тому же это видео для студентов, если начать показывать на таких примерах, то студентов быстро поубавится))))).
      2) По опыту, не стоит такое показывать со старта. Лучше пусть студент чуть больше потратит времени, но набьет руку. А потом уже и IDE под себя настроят и хот кеи. Вообще, когда преподаешь, приходится отучиваться от хот кеев))) и писать код намного более развернуто.
      3) Нуу не знаю, мне больше нравится использовать стандартный стек, так проще программистов заводить на проект. Да и для обучения, я бы никогда не брал что-то не стандартное. Все такие либы уже учатся на работе в реальном проекте если необходимо.
      4) Хм, спасибо не знал. “should” - да, это лишнее.
      5) Не спорю, вариант со своими реализациями стоящий, но нужно понимать, что в этом случае вы теряете весь функционал mock, который не ограничивается просто созданием фейковых объектов. Плюс, подход с mock все же популярнее.
      6) mockito это стандарт от гугла, mockk все же сторонняя либа, поэтому для базового обучения не годится.
      7) "для теста самого это не должно быть важным," - еще как важно, так можно написать тест, который только с виду будет работать).

  • @PavelStr-x5w
    @PavelStr-x5w 7 หลายเดือนก่อน

    Спасибо, все очень понятно объясняете!

  • @pauilegorov
    @pauilegorov 2 ปีที่แล้ว +1

    Спасибо, очень полезная тема!!

  • @MxMayers
    @MxMayers 2 ปีที่แล้ว

    Спасибо за видео!) 14:27 я представил как в офис разработки на 3м этаже, в помещение с джунами заходит сеньер и говорит что тесты сломались, вся команда подрывается и выпрыгивает в окно 😂

  • @АлексейМандрыкин-ч7е
    @АлексейМандрыкин-ч7е 2 ปีที่แล้ว +1

    Спасибо за ваш труд!

  • @TheVincet1998
    @TheVincet1998 ปีที่แล้ว

    Спасибо за видео!

  • @paveln0366
    @paveln0366 2 ปีที่แล้ว +1

    🔥🔥🔥

  • @mikhaillazarev5378
    @mikhaillazarev5378 ปีที่แล้ว

    Спасибо большое.

  • @oleg12395
    @oleg12395 2 ปีที่แล้ว +1

    Как вы поставили кавычки в методе: should return the same data ...

    • @TimofeyKovalenko
      @TimofeyKovalenko  2 ปีที่แล้ว

      JUnit 5 позволяет такое делать.

  • @max__am
    @max__am 2 ปีที่แล้ว +1

    лучший!

  • @namefamily957
    @namefamily957 2 ปีที่แล้ว

    Спасибо! :)

  • @CodeCurator-v8t
    @CodeCurator-v8t ปีที่แล้ว

    Как ставить эти одинарные кавычки под скосом на маке?

  • @mirzorasulpulotov129
    @mirzorasulpulotov129 ปีที่แล้ว

    Что делать? Выдает ошибку Mockito cannot mock/spy because :
    - final class. Хотя класс не финал. Это в котлин.

  • @karamba6936
    @karamba6936 ปีที่แล้ว

    а что вы думаете насчет подхода для каждого класса создавать интерфейс?Нашел канал, там человек опытный пишет такой код, не использует DI библиотеки, тесты тоже пишет. Но код очень отличается от всего, что я видел. Код для меня непонятный и очень расщепленный по файлам отдельным

    • @TimofeyKovalenko
      @TimofeyKovalenko  ปีที่แล้ว

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

  • @alishanvaliani9952
    @alishanvaliani9952 2 ปีที่แล้ว +1

    let's make course about Jetpack Compose!!

  • @__Minecraft_Poseidon__
    @__Minecraft_Poseidon__ 2 ปีที่แล้ว

    Сотый лайк 👍

  • @devkot7040
    @devkot7040 2 ปีที่แล้ว

    Есть вопросы. Вот мы тестируем реализацию UseCase'а(интерактор) .
    1. Почему этот интерактор, не наследуется от какого-то интерфейса интерактора? То есть каким образом мы будем тестировать viewmodel, подставляю туда фейковый интерактор?
    2. Так же хотел спросить, нет ли какого-то либо механизма, который позволяет тестировать интерфейс этого самого интерактора, но, чтобы посредством di каким-то образом в тест inject-ить реализацию этого интерактора. То есть, мы один раз провайдим реализацию интерфейса, а он уже сам тестируется, т.к. в тесте тестировался интерфейс, а не его реализация.

    • @TimofeyKovalenko
      @TimofeyKovalenko  2 ปีที่แล้ว

      1) Просто делаете фейковый интерактор на основе реального и все. Что-бы делать моки не обязательно использовать именно интерфейсы.
      2) "тесте тестировался интерфейс, а не его реализация" - не понял, если честно ваш вопрос, что значит тестировался только интерфейс, интерфейс никогда не тестируется, в этом нет никакого смысла.

    • @devkot7040
      @devkot7040 2 ปีที่แล้ว

      @@TimofeyKovalenko 1) да, можно сделать фейковый интерактор на основе реального, но получается, что если мы заменим реализацию интерактора, то нам придётся во всех тестах его менять вручную, да и в коде собственно.
      2) про тестировался интерфейс. Я имел ввиду, что прописывается работа с интерфейсом в классе теста. Во время компиляции, с помощью DI в класс теста inject- ится реализация этого интерфейса, и уже тестируется реальный класс. Чтобы в следующий раз, когда меняется реализация этого интерфейса, которую мы везде inject- им, нам не пришлось вручную менять тесты.

    • @TimofeyKovalenko
      @TimofeyKovalenko  2 ปีที่แล้ว

      1) Понял вас. Ну тут с одной стороны да, это может быть удобно, но с другой - это очень редкий случай. Дело в том, что для каждой реализации интерактора нужно писать новый тест. А если логика возвращаемых данных остается также, то и врят ли придется писать новую реализацию ;). Да и вообще, лучше все же менять вручную, дело в том, что я часто вижу тесты, которые написаны так, что при изменение тестируемой логики, они тоже "самоизменяются" или подстраиваются под новые требования))), в итоге, от таких тестов нет никакого толка.
      2) Да, в тесте можно использовать зависимости из DI, но опять же вопрос зачем(пункт 1).

  • @DarkWeret
    @DarkWeret 2 ปีที่แล้ว +1

    Спасибо, все очень понятно объясняете) Вопрос: будут ли видео с тестированием viewModel? В котором возвращается какой-нибудь sealed класс state: с loading, success или error? Очень сложная для моего понимания тема)

    • @TimofeyKovalenko
      @TimofeyKovalenko  2 ปีที่แล้ว +1

      Будет но чуть позже, сначала закончим с этим приложением, а вот затем сделаем что-то посложнее.

    • @fournonblondes4089
      @fournonblondes4089 2 ปีที่แล้ว

      viewModel в котором возвращается какой-нибудь sealed класс state: с loading, success или error скажите где об этом можно почитать, лучше на русском. смотрел видео у Филиппа но не всё понял, делать по аналогии сложновато даже.

    • @luckydevil1601
      @luckydevil1601 ปีที่แล้ว

      @@fournonblondes4089 это который Philipp Lackner?)

  • @nikitatkachenko1694
    @nikitatkachenko1694 2 ปีที่แล้ว

    Привет, я начинающий андроид разработчик и для меня этот канал как алмаз среди груды мусора)
    Посмотрев твои уроки начал переделывать свой пет-проект на клин, но столкнулся с проблемой(
    У меня в проекте есть работа с API(retrofit+moshi+okhttp), если я правильно понял то это относиться к "data" слою ?
    Короче говоря, мои модельки для работы с API лежат в слое "data", а также эти модели нужны для слоя "domain", так как там описан интерфейс репозитория, как мне достучаться до этих моделек если по правильному я не могу юзать модели "data" в "domain" ?
    Заранее спасибо за ответ !

    • @TimofeyKovalenko
      @TimofeyKovalenko  2 ปีที่แล้ว +2

      Да, API(retrofit+moshi+okhttp) это часть слоя "data" и у них есть свои модельки, тут все правильно. Но, модельки, которые лежат в слое "data", никакому больше кроме как слою "data" не должны быть видны. Интерфейс репозитория должен возвращать модельки "domain". То есть в реализации репозитория вы должны конвертнуть "data" модельки в "domain" и вернуть их из методов репозитория. Domain главный слой и задает всем правила работы, тоесть репозиторий должен отдать то, что хочет домен. Поэтому "data" модельки используются чисто для внутренней работы "data" слоя.

    • @nikitatkachenko1694
      @nikitatkachenko1694 2 ปีที่แล้ว +1

      @@TimofeyKovalenko спасибо за ответ, а сможешь как-то показать на практике как правильно внедрить работу з api, room, fireBase ?! Думаю будет полезно и информативно)

    • @TimofeyKovalenko
      @TimofeyKovalenko  2 ปีที่แล้ว +2

      Да, есть планы на такие темы, но чуть позже.

  • @PandaTop.
    @PandaTop. 2 ปีที่แล้ว +1

    Дякую

  • @garifzyanovrr
    @garifzyanovrr ปีที่แล้ว

    Тимофей привет!
    Выдает ошибку,
    Expected :...cleanarchitecturelearnapp.domain.models.UserName@632ceb35
    Actual :...cleanarchitecturelearnapp.domain.models.UserName@1c93f6e1
    Все как у тебя в коде.

    • @TimofeyKovalenko
      @TimofeyKovalenko  ปีที่แล้ว +1

      Вы сравниваете объекты, а нужно сравнивать содержимое этих объектов.

    • @decemd1946
      @decemd1946 ปีที่แล้ว +1

      в дата классе метод equals(==) содержимое сравнивает