Всем привет, мы снова встретились, я ваш друг Цюаньчжаньцзюнь.
Если у вас другое мнение, не сомневайтесь, что вы правы, а я нет.
1. Общий метод написания модульных тестов
2. Этапы модульного тестирования
3. Анализируйте и оптимизируйте общие методы написания модульных тестов.
4. Лучший способ написания модульных тестов: макетировать базу данных + не запускать Spring + оптимизировать скорость тестирования + не вводить компоненты проекта.
Вам, как бэкенд-программисту Java, обязательно нужно писать модульные тесты. Позвольте мне сначала привести типичный пример модульного теста:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@Transactional
@Rollback(true) // Транзакции автоматически откатываются. Значение по умолчанию — true. Нет необходимости писать
public class HelloServiceTest {
@Autowired
private HelloService helloService;
@Test
public void sayHello() {
helloService.sayHello("zhangsan");
}
В этом примере 4 ошибки: (Все ошибки в этой статье относятся к нестандартным. На самом деле писать такие модульные тесты можно, но это не стандартизировано и не демонстрирует отличных навыков программирования всех здесь присутствующих. )
1. @Autowired запускает Spring
2. @SpringBootTest запускает среду SpringBoot, а groups=Application.class запускает весь проект.
3. Вы можете узнать, что база данных вызывается через @Transactional
4. Нет утверждения Assert
1、использовать@RunWith(SpringRunner.class)указано вSpringосуществляется в среде Юнит-тест,такSpringфазасосредоточиться решение будет признано и вступит в силу
2、Затемиспользовать@SpringBootTest,Он сканирует весеннюю конфигурацию приложения.,И создайте полный контекст Spring.
3. Через @SpringBootTest мы можем указать класс запуска,или дать@SpringBootTestПараметрыwebEnvironmentнаделять ЗначениеSpringBootTest.WebEnvironment.RANDOM_PORT,так ВоляЗапустите веб-контейнер и прослушайте случайный порт.,в то же время,для насАвтоматически подключить компонент типа TestRestTemplate.чтобы помочь нам отправитьтестпросить。
Если проект немного сложнее, с несколькими модулями типа Spring Cloud, а также использует такие вещи, как кеширование, шардинг, микросервисы, кластерное распределение и т. д., и тогда конфигурация компьютера немного хуже, тогда запуск-выполнение-тестирование Время выполнения каждого модульного теста будет достаточным, чтобы вы могли пойти выпить чашку чая, прежде чем вернуться.
Или ваш проект использует аннотацию @Component (она будет создана/запущена при запуске проекта SpringBoot)
Класс запуска также определяет классы, экземпляры которых создаются при запуске.
В этом аннотированном классе @Component есть несколько методов потока.,С классом ApplicationStartup, определенным в классе запуска, началось,ТакПри выполнении модульного тестирования из-за влияния многопоточных задач данные в вашей базе данных могут быть изменены, даже если вы используете аннотацию отката транзакции @Transactional.。Проблема у меня есть:Когда я бегу Юнит-тесткогда,Другие классы в коде продолжают получать сообщения activeMQ в многопоточном режиме.,Затем обновите соответствующие данные в библиотеке данных. Перекрывается с процессом выполнения модульного теста,Вызывает сбой модульного теста. Когда другие члены команды работают с библиотекой данных,Еще потому, что я случайно запустил многопоточность и изменил библиотеку данных.,Вызвал трудности в разработке.
Кроме того, прилагается исходный код @Component, так что вы можете узнать его кстати.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
//Это значение может использоваться как имя логического компонента (т.е. класса) и конвертироваться в Spring во время автоматического сканирования bean,
//Это эквивалентно<bean id="" class="" />вid
String value() default "";
}
@Component — это метааннотация, что означает, что можно аннотировать другие аннотации классов, например @Controller @Service @Repository @Aspect. Официальные слова таковы: классы с этой аннотацией считаются компонентами. При использовании конфигурации и сканирования путей к классам на основе этой аннотации будут созданы экземпляры этих классов. Другие аннотации уровня класса также можно идентифицировать как специальный тип компонента, например @Repository @Aspect. Следовательно, @Component может аннотировать другие аннотации классов.
Позвольте мне сначала взглянуть на рисунок выше, чтобы показать, сколько времени занимает запуск модульного теста. Затем мы сравниваем и выбираем лучший способ написания модульных тестов. На моем 6-летнем ноутбуке запуск модульного теста занимает почти минуту, но после оптимизации кода это занимает всего несколько секунд. Вот как оптимизировать:
первый,Нам необходимо уточнить конечную цель юнит-теста.,то естьПолное отсутствие в базе данных!Полное отсутствие в базе данных!Полное отсутствие в базе данных!Во-вторых,Юнит-тест предназначен для тестирования только одного метода (небольшого модуля) определенного класса.,В процессе теста,Давай больше ничего не начнем,Быть свободным от возможного вмешательства других факторов в проект.
Так что можно обнаружить, что приведенный выше пример — просто оскорбление юнит-теста, и так его пишут только самые младшие школьники. Как мы все знаем, каждый здесь способен стать архитектором. Далее мы будем критиковать этот ошибочный юнит-тест построчно, пять раз в секунду:
@Autowired
private HelloService helloService;
Этот @Autowired просто лишний! Вот с чего начинается весна. Когда раньше не было @Autowired, нам нужно было настроить свойства bean-компонента следующим образом:
<property name="имя атрибута" value=" значение атрибута"/>
Этот метод содержит много кода и громоздкую настройку, поэтому в Spring 2.5 появилась аннотация @Autowired.
Принцип @Autowired
При запуске Spring IOC контейнер автоматически загружает постпроцессор AutowiredAnnotationBeanPostProcessor. Когда контейнер сканирует @Autowied, @Resource или @Inject, он автоматически находит необходимый bean-компонент в контейнере IOC и собирает атрибуты объекта.
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
Что следует отметить:
1、существоватьиспользовать@Autowiredчас,Компонент соответствующего типа, который будет автоматически введен, сначала будет запрошен в контейнере IOC.
2. Если результат запроса равен единице, соберите компонент со значением атрибута, указанным @Autowired.
3. Если результатов запроса несколько, @Autowired выполнит поиск по имени атрибута.
4. Если результат запроса пуст, будет выдано исключение. Решение: используйте require=false
Тогда возникает проблема,Мы просто собираемся написать модульный тест,Зачем начинать весну? первый,запускатьSpringтолько заставит тебяrun->Junit Программа тормозит при тестировании. Это одна из причин, почему модульные тесты выполняются так медленно при каждом запуске. Тогда модульный тест проверяет только методы определенного класса. Запуск Spring совершенно избыточен, поэтому нам нужен только соответствующий экземпляр класса сущности. Когда нам нужно внедрить компонент, мы напрямую используем new следующим образом:
@Autowired
private HelloService helloService;
Изменить на:
private HelloService helloService = new HelloServiceImpl();
// Этот HelloServiceImpl является соответствующим классом реализации для каждого из ваших интерфейсов.
@SpringBootTest(classes = Application.class)
Этот @SpringBootTest просто преступен! Это виновник того, что модульный тест работает очень медленно при каждом запуске.,Поверьте мне,удали это свое Юнит-тест Скорость улетит быстро。@SpringBootTest, как и @Autowired, полностью избыточен в модульных тестах.,Две вещи, которые совершенно не сочетаются друг с другом! Каждый раз, когда модульный тест сначала запускает SpringBoot
Тогда давайте взглянем на исходный код @SpringBootTest.
Примерное значение:
1. @SpringBootTest используется в проекте SpringBoot. Он считывает атрибуты файла конфигурации на основе @SpringBootContextLoader.
2. Предоставьте следующие функции поверх обычной среды Spring TestContext:
1) Когда в определении нет конкретного @ContextConfiguration(loader=…),использоватьSpringBootContextLoaderкакпо умолчаниюизContextLoader。ContextLoaderизэффект:на самом деле поContextLoaderListenerвыполнение вызовакорневой контекст приложенияиз Работа по инициализации。
2) Если вложенный @Configuration не используется, автоматически выполняется поиск @SpringBootConfiguration без указания явного класса.
3) Позволяет определять пользовательские свойства среды с помощью атрибута свойств.
4. Обеспечить поддержку различных режимов веб-среды, включая запуск полностью работающего веб-сервера и прослушивание определенного или случайного порта.
5) Зарегистрируйте компонент TestRestTemplate или WebTestClient для использования полностью работающего веб-сервера в веб-тестах.
Использование
@SpringBootTest(classes = Application.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
Сейчас обычно пишут так
@SpringBootTest(classes = Application.class)
или это
@SpringBootTest
Но как бы там ни было написано, эту аннотацию использовать не следует
классы = Application.class указывает класс запуска,При выполнении здесь,Прочитаю и проанализирую некоторые файлы конфигурации проекта.,Он также подключится к библиотеке данных.,Затем, если у класса запуска есть другие классы запуска, @Component, многопоточность и т. д.,При выполнении модульного тестирования программа не только работает медленно и занимает много времени, но также может привести к изменению данных в вашей базе данных из-за воздействия многопоточных задач, даже если вы используете аннотацию отката транзакции @Transactional.。
@Transactional
@Rollback(true) // Транзакции автоматически откатываются. Значение по умолчанию — true. Нет необходимости писать
Цели модульного теста,то естьПолное отсутствие в базе данных!Если эта аннотацияиспользовать,Все совершенно наоборот.,Как правило, единицей измерения, использующей эту аннотацию, является тест.,После выхода из библиотеки данных будет сообщено о многих ошибках выполнения.
Assertутверждениеиз Использование,Вы можете прочитать этот блог:Использование Assert в модульном тесте
Так как же нам писать модульные тесты?
Сначала поместите правильный пример модульного теста
//@SpringBootTest
//@SpringBootTest(classes = Application.class)
// Этот класс также запускается при запуске класса запуска, поэтому его также необходимо ввести.
//@Import(ApplicationStartup.class)
// Не выполняйте в проекте методы, аннотированные компонентом.
//@TestComponent
// Примечание 1. Аннотация RunWith сохраняется.
@RunWith(SpringRunner.class)
public class HelloServiceTest {
//@Autowired
// Не используйте Autowired, не запускайте контейнер Spring и не создайте экземпляр класса реализации метода, который необходимо реализовать напрямую с помощью new.
private HelloService helloService = new HelloServiceImpl();
@Test
public void sayHello() {
// Чтобы смоделировать EntityManager JPA, необходимо смоделировать все официальные интерфейсы и классы.
EntityManager em = init(helloService);
// Any() заменяет параметры любого типа
Mockito.doReturn("Я — возвращаемое значение моделирования").when(em).findById( any());
// Методы без возвращаемых значений не нужно писать отдельно, поскольку они автоматически моделируются при моделировании классов сущностей.
Mockito.doNothing().when(em).find(any());
helloService.sayHello("zhangsan");
Assert.isTrue(true,"Совершенно правильный модульный тест");
}
EntityManager init(Object classInstance ){
// класс для издевательств
EntityManager em = Mockito.mock(EntityManager.class);
// Укажите класс отражения
Class<?> clazz = classInstance.getClass();
// Получить атрибуты указанного класса
Field field = null;
try {
field = clazz.getDeclaredField("em");
// Значение true затем указывает, что отраженный объект должен быть отменен при использовании Java Проверка языкового доступа.
// Значение false затем указывает, что отраженный объект должен реализовать Java Проверка языкового доступа.
// по умолчанию false
field.setAccessible(true);
// Изменить стоимость частной собственности
field.set(classInstance, em);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
return em;
}
}
// HelloServiceImpl — это класс реализации. Следующий код предназначен только для выражения смысла.
class HelloServiceImpl {
@Autowired
private EntityManager et;
sayHello(String name) {
// Методы работы с библиотекой данных без возвращаемых значений
et.find(name);
// Методы с возвращаемыми значениями
String oldSecondName = et.findById(name.substring(2));
}
}
Вы можете видеть, что аннотация @RunWith сохраняется.
1. @RunWith В JUnit много Runner. Они отвечают за вызов вашего тестового кода. Каждый Runner имеет свои собственные специальные функции. Вам нужно выбрать другой Runner для запуска тестового кода в соответствии с вашими потребностями. Обычно используется SpringRunner.class.
2. Если мы просто проводим обычное тестирование Java и не задействуем Spring Web-проекты, вы можете опустить аннотацию @RunWith, чтобы система автоматически использовала Runner по умолчанию для запуска вашего кода.
Тогда самое главное — Mock. Необходимые для Mock банки уже включены здесь.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
На этом этапе вам потребуются базовые знания Mock. Mock предназначен для моделирования всех шагов по работе с базой данных без выполнения какого-либо SQL. Мы непосредственно моделируем код, который успешно управляет базой данных при выполнении и может имитировать любое возвращаемое значение. основные аннотации.
Пока он локальный и вы пишете свои собственные bean-компоненты, вы можете использовать эту аннотацию. Она будет моделировать все методы работы с базой данных. Если это метод, который не возвращает значение, мы можем его игнорировать. Если существует метод, который возвращает значение, мы можем вернуть ему значение, которое нам нужно смоделировать. Использование следующее:
// Any() заменяет параметры любого типа
Mockito.doReturn("Я — возвращаемое значение моделирования").when(em).findById( any());
// Методы без возвращаемых значений не нужно писать отдельно, поскольку они автоматически моделируются при моделировании классов сущностей.
Mockito.doNothing().when(em).find(any());
Если мы местные и звоним в другие компании, в других местах для нас написаны интерфейсы. Вместо того, чтобы работать с собственной базой данных, мы пишем нам входные параметры, а другие возвращают нам значения, поэтому мы используем это. Его использование такое же, как @MockBean.
Основные различия в использовании между ними:
Тогда то, что мы здесь высмеиваем, — это официальный EntityManager JPA. Чтобы официальные интерфейсы и классы использовались в качестве частных атрибутов в наших классах реализации для управления базой данных, мы можем смоделировать их с помощью этого метода.
EntityManager init(Object classInstance ){
// класс для издевательств
EntityManager em = Mockito.mock(EntityManager.class);
// Укажите класс отражения
Class<?> clazz = classInstance.getClass();
// Получить атрибуты указанного класса
Field field = null;
try {
field = clazz.getDeclaredField("em");
// Значение true затем указывает, что отраженный объект должен быть отменен при использовании Java Проверка языкового доступа.
// Значение false затем указывает, что отраженный объект должен реализовать Java Проверка языкового доступа.
// по умолчанию false
field.setAccessible(true);
// Изменить стоимость частной собственности
field.set(classInstance, em);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
return em;
}
Если ваш проект не такой уж сложный, вам нужно всего лишь добавить аннотацию @MockBean поверх класса, который вы хотите имитировать. Обычно это используется, например.
public class HelloServiceTest {
//@Autowired
// Не используйте Autowired, не запускайте контейнер Spring и не создайте экземпляр класса реализации метода, который необходимо реализовать напрямую с помощью new.
private HelloService helloService = new HelloServiceImpl();
@MockBean
HelloDao dao;
@Test
public void sayHello() {
// Any() заменяет параметры любого типа
Mockito.doReturn("Я — возвращаемое значение моделирования").when(dao).findById( any());
// Методы без возвращаемых значений не нужно писать отдельно, поскольку они автоматически моделируются при моделировании классов сущностей.
Mockito.doNothing().when(dao).find(any());
helloService.sayHello("zhangsan");
Assert.isTrue(true,"Совершенно правильный модульный тест");
}
Этот код может не иметь смысла по сравнению с приведенным выше. Я набрал его случайно. Я хочу выразить следующее: если вам не нужно моделировать официальные интерфейсы и классы для работы с базой данных, вы можете напрямую добавить аннотацию @MockBean или @SpyBean. поверх вашего класса реализации, а затем просто используйте синтаксис Mockito.
Если вы понимаете, о чем я?
Частичная ссылка:
Springboot тест – короткая книга
Подробный анализ аннотации Spring @Component_L Xiaoyun's blog-CSDN blog_@comComponent параметры
Издатель: Лидер стека программистов полного стека, укажите источник для перепечатки: https://javaforall.cn/133517.html Исходная ссылка: https://javaforall.cn