- Integration testing of a database using MariaDB to replace MySql
- Implementation of multilingual application
- Saving files to the application and data about them to the database
Types of testing
What is a test? As Wiki says: “ A test or trial is a way of studying the underlying processes of a system by placing the system in different situations and tracking observable changes in it.” In other words, this is a test of the correct operation of our system in certain situations. Well, let's see what types of testing there are:-
Unit testing is tests whose task is to test each module of the system individually. It is desirable that these be minimally divisible pieces of the system, for example, modules.
-
System testing is a high-level test to test the operation of a larger piece of an application or the system as a whole.
-
Regression testing is testing that is used to check whether new features or bug fixes affect the existing functionality of the application and whether old bugs reappear.
-
Functional testing is checking the compliance of part of the application with the requirements stated in specifications, user stories, etc.
Types of functional testing:
- “white box” test for compliance of part of the application with the requirements with knowledge of the internal implementation of the system;
- “black box” test for compliance of part of the application with the requirements without knowledge of the internal implementation of the system.
- Performance testing is a type of tests that are written to determine the speed at which a system or part of it runs under a certain load.
- Load testing - tests designed to check the stability of the system under standard loads and to find the maximum possible peak at which the application works correctly.
- Stress testing is a type of testing designed to check the functionality of an application under non-standard loads and to determine the maximum possible peak at which the system will not crash.
- Security testing - tests used to check the security of a system (from attacks by hackers, viruses, unauthorized access to confidential data and other pleasures of life).
- Localization testing is localization testing for an application.
- Usability testing is a type of testing aimed at checking usability, understandability, attractiveness and learnability for users. This all sounds good, but how does it work in practice? It's simple: Mike Cohn's testing pyramid is used: This is a simplified version of the pyramid: now it is divided into smaller parts. But today we will not pervert and consider the simplest option.
-
Unit - unit tests used in various layers of the application, testing the smallest divisible logic of the application: for example, a class, but most often a method. These tests usually try to isolate as much as possible from external logic, that is, to create the illusion that the rest of the application is working in standard mode.
Данных тестов всегда должно быть много (больше, чем остальных видов), так How они тестируют маленькие кусочки и весьма легковесные, не кушающие много ресурсов (под ресурсами я имею виду оперативную память и время).
-
Integration — интеграционное тестирование. Оно проверяет более крупные кусочки системы, то есть это либо объединение нескольких кусочков логики (несколько методов or классов), либо корректность работы с внешним компонентом. Этих тестов How правило меньше, чем Unit, так How они тяжеловеснее.
Как пример интеграционных тестов можно рассмотреть соединение с базой данных и проверку правильной отработки методов, работающих с ней.
-
UI — тесты, которые проверяют работу пользовательского интерфейса. Они затрагивают логику на всех уровнях applications, из-за чего их еще называют сквозными. Их How правило в разы меньше, так они наиболее тяжеловесны и должны проверять самые необходимые (используемые) пути.
На рисунке выше мы видим соотношение площадей разных частей треугольника: примерно такая же пропорция сохраняется в количестве этих тестов в реальной работе.
Сегодня подробно рассмотрим самые используемые тесты — юнит-тесты, так How уметь ими пользоваться на базовом уровне должны все уважающие себя Java-разработчики.
- материал о Code Coverage на JavaRush и на Хабре;
- фундаментальная теория тестирования.
- Пишем наш тест.
- Запускаем тест, прошел он or нет (видим, что всё красное — не психуем: так и должно быть).
- Добавляем code, который должен удовлетворить данный тест (запускаем тест).
- Выполняем рефакторинг codeа.
- Задание тестируемых данных (фикстур).
- Использование тестируемого codeа (вызов тестируемого метода).
- Проверка результатов и сверка с ожидаемыми.
assertEquals(Object expecteds, Object actuals)
— проверяет, равны ли передаваемые обьекты.assertTrue(boolean flag)
— проверяет, возвращает ли переданное meaning — true.assertFalse(boolean flag)
— проверяет, возвращает ли переданное meaning — false.assertNull(Object object)
– проверяет, является ли an object нулевым (null).assertSame(Object firstObject, Object secondObject)
— проверяет, ссылаются ли передаваемые значения на один и тот же обьект.assertThat(T t, Matcher<T> matcher)
— проверяет, удовлетворяет ли t условию, указанному в matcher.
Ключевые понятия юнит-тестирования
Покрытие тестов (Code Coverage) — одна из главных оценок качества тестирования applications. Это процент codeа, который был покрыт тестами (0-100%). На практике многие гонятся за этим процентом, с чем я не согласен, так How начинается навешивание тестов там, где они не нужны. Например, у нас в сервисе есть стандартные CRUD (create/get/update/delete) операции без дополнительной логики. Эти методы — сугубо посредники, делегирующие работу слою, работающему с репозиторием. В данной ситуации нам нечего тестировать: разве то, что вызывает ли данный метод — метод из дао, но это не серьёзно. Для оценки покрытия тестами обычно используют дополнительные инструменты: JaCoCo, Cobertura, Clover, Emma и т.д. Для более детального изучения данного вопроса держи пару годных статей:Этапы тестирования
Тест состоит из трёх этапов:Среды тестирования
Итак, теперь ближе к делу. Для Java доступно несколько сред тестирования (фреймворков). Самые популярные из них — JUnit и TestNG. Для нашего обзора мы используем: JUnit тест представляет собой метод, содержащийся в классе, который используется только для тестирования. Класс, How правило, называется так же, How и класс, который он тестирует с +Test в конце. Например, CarService→ CarServiceTest. Система сборки Maven автоматически включает такие классы в тестовую область. По сути этот класс и называется тестовым. Немного пройдёмся по базовым annotationм: @Test — определение данного метода в качестве тестируемого (по сути — метод, помеченный данной аннотацией и есть модульный тест). @Before — помечается метод, который будет выполняться перед каждым тестом. Например, заполнение тестовых данных класса, чтение входных данных и т. д. @After — ставится над методом, который будет вызывать после каждого теста (чистка данных, восстановление дефолтных значений). @BeforeClass — ставится над методом — аналог @Before. Но этот метод вызывается лишь однажды перед всеми тестами для данного класса и поэтому должен быть статическим. Он используется для выполнения более тяжелых операций, How например подъем тестовой БД. @AfterClass — противоположность @BeforeClass: исполняется один раз для данного класса, но исполняется после всех тестов. Используется, например, для очистки постоянных ресурсов or отключения от БД. @Ignore — отмечает, что метод ниже отключен и будет игнорироваться при общей прогонке тестов. Используется в разных случаях, например, если изменor базовый метод и не успели переделать под него тест. В таких случаях ещё желательно добавить описание — @Ignore("Some description"). @Test (expected = Exception.class) — используется для отрицательных тестов. Это тесты, которые проверяют, How ведёт себя метод в случае ошибки, то есть тест ожидает, что метод выкинет некоторое исключение. Такой метод обозначается аннотацией @Test, но с указанием ошибки для отлова. @Test(timeout=100) — проверяет, что метод исполняется не более чем 100 миллисекунд. @Mock — используется над полем класс для задания данного an object моком (это не из Junit библиотеки, а из Mockito), и если нам будет необходимо, мы зададим поведение мока в конкретной ситуации, непосредственно в методе теста. @RunWith(MockitoJUnitRunner.class) — метод ставится над классом. Он и является кнопкой для прогона тестов в нем. Runner-ы могут быть различными: например, есть такие: MockitoJUnitRunner, JUnitPlatform, SpringRunner и т. д.). В JUnit 5 аннотацию @RunWith заменor более мощной аннотацией @ExtendWith. Взглянем на некоторые методы сравнения результатов:assertThat(firstObject).isEqualTo(secondObject)
Здесь я рассказал о базовых методах, так How остальные — это различные вариации приведенных выше.
Практика тестирования
А теперь давайте рассмотрим приведенный выше материал на конкретном примере. Будем тестировать метод для сервиса — update. Рассматривать слой дао не будем, так How он у нас дефолтный. Добавим стартер для тестов:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
Итак, класс сервиса:
@Service
@RequiredArgsConstructor
public class RobotServiceImpl implements RobotService {
private final RobotDAO robotDAO;
@Override
public Robot update(Long id, Robot robot) {
Robot found = robotDAO.findById(id);
return robotDAO.update(Robot.builder()
.id(id)
.name(robot.getName() != null ? robot.getName() : found.getName())
.cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
.producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
.build());
}
}
8 — вытягиваем обновляемый обьект из БД 9-14 — создаём an object через билдер, если в приходящем an objectе есть поле — задаем его, если нет — оставляем то, что есть в БД И смотрим наш тест:
@RunWith(MockitoJUnitRunner.class)
public class RobotServiceImplTest {
@Mock
private RobotDAO robotDAO;
private RobotServiceImpl robotService;
private static Robot testRobot;
@BeforeClass
public static void prepareTestData() {
testRobot = Robot
.builder()
.id(123L)
.name("testRobotMolly")
.cpu("Intel Core i7-9700K")
.producer("China")
.build();
}
@Before
public void init() {
robotService = new RobotServiceImpl(robotDAO);
}
1 — наш Runner 4 — изолируем сервис от слоя дао, подставляя мок 11 — задаем для класса тестовую сущность (ту, которую мы будем юзать в качестве испытуемого хомячка) 22 — задаём an object сервиса, который мы и будем тестить
@Test
public void updateTest() {
when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
Robot robotForUpdate = Robot
.builder()
.name("Vally")
.cpu("AMD Ryzen 7 2700X")
.build();
Robot resultRobot = robotService.update(123L, robotForUpdate);
assertNotNull(resultRobot);
assertSame(resultRobot.getId(),testRobot.getId());
assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
assertEquals(resultRobot.getProducer(),testRobot.getProducer());
}
Здесь мы видим четкое разделение теста на три части: 3-9 — задание фикстур 11 — выполнение тестируемой части 13-17 — проверка результатов Подробнее: 3-4 — задаём поведение для мока дао 5 — задаём экземпляр, который мы будем апдейтить поверх нашего стандартного 11 — используем метод и берём результирующий экземпляр 13 — проверяем, что он не ноль 14 — сверяем айди результата и заданные аргументы метода 15 — проверяем, обновилось ли Name 16 — смотрим результат по cpu 17 – так How в экземпляре для обновления мы не задавали это поле, оно должно остаться прежним, проверяем это. Запускаем: Тест зелёный, можно выдыхать)) Итак, подведём итоги: тестирование улучшает качество codeа и делает процесс разработки более гибким и надёжный. Представьте себе, How много сил мы потратим при изменении дизайна программного обеспечения с сотнями файлов классов. Когда у нас есть модульные тесты, написанные для всех этих классов, мы можем уверенно провести рефакторинг. И самое главное — это помогает нам легко находить ошибки во время разработки. Гайз, на этом у меня сегодня всё: сыпем лайки, пишем комменты)))
GO TO FULL VERSION