1. Введение
В последнее время я разрабатываю проект, и итеративно выпускаю версию каждые две недели.Это все нормально, но есть особое требование к качеству проекта.Покрытие юнит-тестами фонового кода должно достигать 80%, и этот показатель используется как жесткость запуска. В то время я думал, что это требование было довольно забавным: вам все еще нужно написать один тест для интернет-разработки? ! Только после того, как я в частном порядке проконсультировался с моими партнерами по бизнесу xx nail, я понял, что они уже какое-то время внедряют здесь такие стандарты исследований и разработок, иЭто действительно улучшило качество исследований и разработок, гарантировало качество кода и обслуживания в фоновом режиме и уменьшило задержку доставки из-за проблем с качеством (на самом деле, я полностью согласен). Просто используйте эту возможность, чтобы разобраться в содержании знаний, связанных с модульным тестированием в целом.
2. Знание модульного тестирования
2.1 Модульное тестирование
Модульное тестирование относится к проверке и проверке наименьшей тестируемой единицы программного обеспечения. Что касается значения модуля в модульном тестировании, модуль относится к классу реализации в Java и может относиться к окну или меню в графическом программном обеспечении.
Модульное тестирование — это деятельность по тестированию самого низкого уровня, которая должна выполняться во время разработки программного обеспечения.Независимые модули программного обеспечения будут тестироваться изолированно от остальной части программы, часто в сочетании с проверками кода, статическим и динамическим анализом и т. д.
- Статический анализ: изучая код проекта, чтобы найти ошибку или собрать некоторые метрики, обычно используются CheckStyle/FindBugs и другие инструменты.
- Динамический анализ: предоставляет информацию о трассировках выполнения, временном анализе и покрытии путем наблюдения за действиями программного обеспечения во время его работы.
В сегодняшнюю эпоху победы за счет быстрого выполнения проектов быстрее означает возможность воспользоваться рыночными возможностями, поэтому многие мелкие партнеры будут озадачены: если функции нельзя написать, зачем писать модульные тесты? На самом деле, преимущества написания модульных тестов перевешивают недостатки:
- Повысить скорость разработки, выполнить тест автоматически и повысить эффективность выполнения тестового кода;
- Улучшение качества программного кода, в нем используются небольшие версии, выпущенные для интеграции, легко реализуемая кадровая отладка. При этом вводится понятие рефакторинга, чтобы сделать код чище и гибче;
- Повышение надежности системы может в определенной степени обеспечить корректность кода.
В настоящее время модульное тестирование можно разделить на тестирование классов, функциональное тестирование и тестирование интерфейса Соотношение между размером выгод (улучшение качества кода проекта) таково: тестирование классов
С точки зрения итерации цикла разработки проекта, чем больше ошибок будет исправлено на более позднем этапе, тем выше будет стоимость, поэтому модульное тестирование сдвинет процесс тестирования влево, что позволит разработчикам раньше контролировать качество проектирования и реализации. , снижение затрат на ремонт.
С точки зрения программной системы сложность бизнеса будет становиться все выше и выше, а различные ассоциации и зависимости будут накладываться слой за слоем, что приведет к увеличению неуправляемых показателей качества. стабильное состояние может значительно снизить фактор риска суперпозиции, тем самым повышая стабильность и качество всей системы. Когда вы это понимаете, написание модульных тестов становится такой же естественной привычкой разработки, как и написание функционального кода.
2.2 Покрытие кода
**Покрытие кода — это метод косвенного измерения качества программного обеспечения путем расчета доли исходного кода, выполненного в процессе тестирования, по отношению к общему исходному коду. **Потенциально может обеспечить качество реального продукта при обеспечении качества теста.На основании этого можно найти места в программе, не проверенные тест-кейсами, и в дальнейшем создавать новые тест-кейсы для увеличения охвата . По своей природе оно относится к категории тестирования белого ящика, то есть тестовые примеры в основном разрабатываются в соответствии с внутренней структурой исходного кода, а различные части программного обеспечения тестируются путем проектирования различных входных данных. Распространенные языки программирования имеют соответствующие инструменты тестирования покрытия кода. Анализ на основе покрытия кода может сделать следующее:
- Проанализируйте код непокрытых частей, сделайте вывод о том, достаточен ли дизайн теста на ранней стадии, ясны ли требования/дизайн, нет ли неправильного понимания дизайна теста и т. д., а затем выполните дополнительный дизайн тестового примера.
- Обнаружение ненужного кода в программе может устранить путаницу в дизайне кода, напомнить дизайнеру/разработчику о необходимости прояснить логическую взаимосвязь кода и улучшить качество кода.
Многие инструменты разработки, такие как IDEA, имеют встроенные инструменты для статистического охвата.
3. Фреймворк модульного тестирования
3.1 Среда модульного тестирования Java
Большинство фреймворков модульного тестирования включают следующие основные компоненты:
-
TestRuner
: Отвечает за выполнение сценариев модульных тестов и отчетность о результатах выполнения тестов; -
TestFixture
: Доступен в виде набора тестовsetUp
()а такжеtearDown()
метод обеспечения того, чтобы выполнение двух тестовых случаев было независимым друг от друга и не влияло друг на друга; -
TestResult
: этот компонент используется для сбора результатов выполнения каждого TestCase; -
Test
: так какTestSuite
а такжеTestCase
Родительский класс предоставляет метод run() для вызовов TestRunner; -
TestCase
: классы, открытые для пользователей, пользователи могут написать свою собственную логику тестового примера, наследуя TestCase; -
TestSuite
: Обеспечить набор функций для управления testCase.
Java
В отрасли существует множество фреймворков модульного тестирования, большинство из которых основано на идеях дизайна фреймворка Junit.Junit4/Junit5/TestNG
Большинство этих фреймворков в настоящее время поддерживают аннотации и параметризованные тесты, указывают разные значения тестов во время выполнения для запуска модульных тестов и работают с инструментами сборки, такими как Maven/Gradle.
3.2 Простой пример Junit
public class JunitDemoTest {
@Rule
public Timeout timeout = new Timeout(1000);
@Before
public void init() {
//...
}
@After
public void destroy() {
//....
}
@Test
public void testAssertArrayEquals() throws InterruptedException {
byte[] str1 = "test1".getBytes();
byte[] str2 = "test2".getBytes();
byte[] str3 = "test".getBytes();
assertTrue("true", String.valueOf(str1).equals(String.valueOf(str3)));
assertFalse("false", String.valueOf(str1).equals(String.valueOf(str2)));
assertArrayEquals("false - byte arrays not same", str1, str2);
Thread.sleep(50000);
}
}
3.2 Основной принцип реализации Junit
Когда среда Junit запустится, она будет вызванаJunitCore#run(Runner runner)
Методы.
вRunner
Это абстрактный класс, реализованный по-разному для разных платформ и версий, включаяJunit4ClassRunner
класс, который наследуетParentRunner
изBlockJUnit4ClassRunner
Добрый/SpringRunner
Добрый/Suit
класс, который вызывает соответствующую реализациюrun
метод.
в сбореStatement
деинициализирует правила предварительного запуска и правила предварительного запуска и сопоставления.
вызовchildrenInvoker
После этого одиночная тестовая задача будет открыта с использованием пула потоков задач. существуетgetFilteredChildren
Метод получит все методы в классе модульного теста, которые необходимо протестировать, и вызовет их циклически.runChild
Метод проверит метод и поместит его в пул потоков задач для выполнения.
существуетrunChild
пойду сноваmethodBlock
Генерируется для каждого метода тестированияStatement
объект,
methodBlock
Сначала он сгенерирует объект тестового класса, а затем сгенерирует выполнение решения до/после/правила/реального целевого метода, чтобы сформировать цепочку выполнения метода.
RunBefores/RunAfters
Классы наследуютсяStatement
класс, который использует метод цепочки для формирования цепочки выполнения,
использоватьJava
Механизм отражения вызывает тестовый метод.
существуетrunLeaf
вызовет оценку сгенерированного объекта оператора, который является началом цепочки выполнения метода. и передать результат вызова черезEachTestNotifier
Объект отправляется, и здесь в основном используется режим слушателя, например, будет отправлена информация о результате операции, сбое или исключении.
Несколько других фреймворков также основаны наJunit
Расширение этого базового процесса обеспечивает большое удобство для написания модульных тестов в повседневной разработке.Однако при разработке крупномасштабных проектов также могут возникнуть некоторые проблемы, такие как совместная разработка, например, некоторые зависимые интерфейсы или базовые модули не были учтены. разработанные. , модульные тесты, написанные таким образом, недействительны, и некоторые данные/результаты должны быть «подделаны», чтобы выполнить эти основные зависимости. Чтобы решить эти проблемы,Mock
Появилась технология.
4. Мок-фреймворк
4.1 Введение в макет
Mock
Обычно означает, что при тестировании объекта S мы создаем некие поддельные объекты для имитации взаимодействия с S, и этиMock
Поведение объекта — это то, что мы устанавливаем заранее и ожидаемо. через этиMock
Объект, чтобы проверить, правильно ли работает S в нормальной логике, ненормальной логике или стрессовых ситуациях. представлятьMock
Самым большим преимуществом является:Mock
Поведение фиксировано, оно гарантирует, что при доступе кMock
При вызове метода всегда можно получить ожидаемый результат, который возвращается напрямую без какой-либо логики. Обычно предоставляются некоторые из следующих преимуществ:
- Изолируйте ошибки в других модулях, чтобы вызвать ошибки тестирования в этом модуле.
- Изолируйте статус разработки других модулей, пока интерфейс определен, независимо от того, завершена их разработка или нет.
- Для некоторых более медленных операций вы можете использовать
Mock Object
Вместо этого быстро возвращайтесь.
Для тестирования распределенной системы используйтеMock Object
Есть еще два важных преимущества: - пройти через
Mock Object
Можно преобразовать некоторые распределенные тесты в локальные тесты. - Буду
Mock
Используемый для стресс-тестирования, он может решить проблему, заключающуюся в том, что тестовый кластер не может имитировать крупномасштабное давление онлайн-кластера.
Платформа Mock включает EasyMock/JMock/Mokito/PowerMokito, а текущий проект использует PowerMokito.
4.2 Простой фиктивный пример
полныйMock
, включая четыре шага постановки целей -> установка условий потребления -> ожидаемые результаты возврата -> потребление и проверка возвращаемых результатов, мы используемMockito
инструменты для реализации простейшего примера,Mockito
главным образом черезStub
Для нагромождения имя и параметры метода используются для точного определения местоположения тестовой заглушки и возврата ожидаемого значения.
@Data
public void TestDao {
public User getUser(String uid) { return new User("testUser"); }
@Data
public void TestService {
private TestDao testDao;
public User getUser(String uid) { return testDao.getUser(uid); }}
@RunWith(PowerMokitoRunner.class)
public void MockDemoTest {
@InjectMock
private TestService testService
@Mock
private TestDao testDao;
@Before
public void init() {
Mockito.when(testDao.getUser(any())).thenReturn(new User("test2"));
}
@Test
public void test1() {
//调用方法时,会调用到代理对象返回刚才的Mock数据
User userInfo = testService.getUser("uid");
}
}
В основном это соответствует следующим четырем шагам
-
установить цели > пользователь пользователя
-
Установить условия потребления -> testDao.getUser(any())
-
Ожидаемый результат возврата -> thenReturn(..)
-
Потребляйте и проверяйте возвращаемый результат -> testDao.getUser("uid")
4.3 Основной принцип реализации PowerMockito
PowerMockito
Основной принцип платформы заключается в использовании инструмента байт-кода CGLIGB на основе режима прокси для реализации прокси, который генерирует целевой объект или метод.При использовании, когда вызывать инициализацию, он будет соответствовать, а затем возвращаться в соответствии с возвратом.
PowerMockitoRunner
наследоватьJunit#Runner
класс, который будет вызываться при запускеrunwith
метод, платформа Junit загрузитPowerMokito#PowerMockJUnitRunnerDelegateImpl
Добрый.
существуетWhitebox#findSingleFieldUsingStrategy
принесу@Mock/@InjectMocks
Параметры аннотации найдены и инициализированы.
Наконец вWhitebox#newInstance
будет генерироватьMock
объект.
Как правило, «нагромождение» выполняется при инициализации модульного теста, напримерwhen...thenReturn....
генерироватьStubbing
Когда он вернется, ход укладки будет обновлен, и будет сгенерирован соответствующий прокси.
performStubbing
позвонюcreateMock
Создание прокси-объектов.
Он будет вызываться при работе в качестве проксиMockMaker
поколение классаMock
объект.
PowerMockMaker/CglibMockMaker
все сбудетсяMockMaker
Интерфейс, в котором PowerMockito поддерживает различные методы перезаписи улучшения байт-кода. В настоящее время используется метод CGLIB.
ClassImposterizer#createProxyClass
использоватьCGLIB
инструмент байт-кодаEnhance
Класс проксирует целевой класс модульного теста и вставляет методы для перехвата объекта класса.
Определен класс перехвата метода.
Таким образом, в будущем будет создан новый объект прокси-класса.При сопоставлении метода будет возвращен ожидаемый результат, потому что существует еще много реализаций всего фреймворка PowerMockito.Вот относительно простой пример для легкого понимания :
public class PowerMockito {
private static Map<Invocation, Object> results =
new HashMap<Invocation, Object>();
private static Invocation lastInvocation;
public static <T> T mock(Class<T> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new MockInterceptor());
return (T)enhancer.create();
}
private static class MockInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Invocation invocation = new Invocation(proxy, method, args, proxy);
lastInvocation = invocation;
if (results.containsKey(invocation)) {
return results.get(invocation);
}
return null;
}
}
public static <T> When<T> when(T o) {
return new When<T>();
}
public static class When<T> {
public void thenReturn(T retObj) {
results.put(lastInvocation, retObj);
}
}
}
5. Советы по написанию модульных тестов
Вот несколько советов по написанию модульных тестов:
- Менталитет надо исправлять.Написание одного теста не пустая трата времени, а для лучшего улучшения качества дизайна и реализации;
- Фоновая разработка представляет собой иерархическую структуру, которая требует субподрядного управления тестовыми примерами и написания отдельных тестов по уровням;
- Чтобы максимизировать охват кода, подумайте о наборах тестовых данных, таких как границы/исключения/сбои и т. д.;
- Не злоупотребляйте Mock, вызывайте то, что должно быть вызвано, и вы не можете игнорировать первоначальную цель написания модульных тестов для достижения покрытия методов;
- Более широкое использование инструментов генерации одиночных тестов для повышения эффективности написания, по этой причине автор также инкапсулирует компонент генерации.
6. Резюме
Эта статья основана на знаниях об одиночном тесте, недавно использовавшемся при разработке проекта.Она в основном описывает определение и функцию одиночного теста.Теперь используются основные принципы использования и реализации фреймворков Junit и Mock, разработанных на фоне Java. Наконец, автор описывает автора.Некоторый опыт и предложения по самостоятельному написанию юнит-тестов.
использованная литература
- blog.CSDN.net/u伱first/AR…Сравнение фреймворков модульного тестирования
- blog.CSDN.net/QQ_26295547…Подробное введение в junit framework
- Специальность Youzan.com/code-cover…Говоря о покрытии кода
- zhuanlan.zhihu.com/p/144826192покрытие кода
- blog.CSDN.net/WeChat_4236…
- blog.CSDN.net/WeChat_4433…Как писать хорошие модульные тесты