spock mocking stubbing
Знущання, заколювання та шпигунство зі Споком:
Параметризоване тестування в Spock Framework було детально пояснено в цьому Серія навчальних посібників зі Спока .
Знущання та удари є одним з найважливіших будівельних блоків великих модульних тестів. Підставка для знущань та підзйомок - це як вишня на торті для каркасу.
Для існуючих фреймворків, таких як JUnit, JBehave тощо, підтримка макетів і заглушок не виходить з коробки, отже, розробник повинен використовувати сторонні бібліотеки, такі як Mockito, PowerMock, EasyMock тощо, щоб використовувати їх у одиничні тести.
Для того, щоб зрозуміти насмішки та заглушки та випадки їх використання, ви можете поглянути на нашу серію Підручник з Mockito .
У цьому підручнику ми дізнаємось більше про вбудовані функції знущання та придушення, інтегровані в саму бібліотеку Spock, що, у свою чергу, дозволить використовувати простіший синтаксис Groovy і тим самим зменшить потребу додавати / включати будь-які інші 3рдпартійні бібліотеки.
Ви завжди можете включити до своїх тестів інші фреймворки для насмішок, оскільки весь дійсний код Java також є дійсним кодом Groovy.
Що ви дізнаєтесь:
- Заявка під тестом
- Знущання над Споком
- Забивання в Спок
- Шпигунство в Спок
- Висновок
- Вихідний код програми
- Рекомендована література
Заявка під тестом
Давайте спершу визначимо зразок Java-програми, яку ми будемо тестувати, використовуючи макети та заглушки в рамках Spock.
Ми працюватимемо над додатком StudentGradeCalculator, який бере загальний бал з абстрагованої бази даних для даного студентського посвідчення та має просту логіку присвоєння оцінок залежно від значення загального балу. Ми будемо використовувати інтерфейс бази даних, який має декілька методів для отримання та оновлення оцінок та оцінок учнів.
Код програми буде доступний в останньому розділі цього посібника.
Знущання над Споком
Відео-посібник
У цьому розділі ми побачимо, як створити екземпляр та ініціалізувати Mocks у фреймворці Spock і як перевірити взаємодію на макеті, тобто перевірка викликів макетів відбулася відповідно до очікувань тестованого методу.
З Mocks вам не потрібно робити багато налаштувань, але ви можете перевірити взаємодію, яка відбулася з макетними об’єктами, що надходять у тестоване додаток.
За допомогою макетів ви можете робити такі речі:
- З якими аргументами називали насмішки?
- Якою була загальна кількість викликів тощо?
- З'ясування порядку знущань.
Давайте подивимось простий приклад StudentGradeCalculator, де ми надаємо обминутий об'єкт реалізації бази даних і перевіряємо взаємодію з Mock. Ми спробуємо зрозуміти насмішливі особливості на простих прикладах.
Зверніть увагу, що всі перевірки взаємодії повинні відбуватися в блоці 'тоді' за домовленістю.
Нижче наведено код тестованого методу (що буде називатися в коли: ”Блок)
public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; }
# 1) Перевірка взаємодії з точними аргументами: Спершу перевіримо взаємодію з точно очікуваними аргументами. Тут ми будемо очікувати, що висміювані методи будуть викликані з точними аргументами (відповідно до потоку виконання методу).
Тут “ база даних студента ”- макет інтерфейсу бази даних, для якого ми перевіряємо взаємодію.
def 'illustrate mocks for interaction verification with arguments'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade('123','C') 1*studentDatabase.getStudentGrade('123') }
Як показано вище, ми перевіряємо з точними аргументами, так що знущальна реалізація повинна бути викликана за допомогою. Будь-які зміни в цих аргументах призведуть до невдалого тесту, а журнал помилок відображає відповідну причину.
Спробуємо змінити оцінку в updateStudentGrade 'До' A 'замість фактично названого' C 'і подивіться, яку помилку ми отримуємо, коли тест виконується.
Too few invocations for: 1*studentDatabase.updateStudentGrade('123','A') (0 invocations) Unmatched invocations (ordered by similarity): 1 * studentDatabase.updateStudentGrade('123', 'C') 1 * studentDatabase.getStudentScores('123')
Він покаже помилку типу 'Забагато викликів', оскільки він не може знайти фіктивне виклик із наданими аргументами.
# два) Тепер давайте подивимося, як перевірити взаємодію Mock без надання фактичних значень аргументів, тобто те, що нас цікавить, це просто знати, що макет був викликаний у методі, але не з якими аргументами.
Цей тип вимог є найпоширенішим під час написання модульних тестів для фактичного виробничого коду, оскільки не завжди легко визначити фактичні аргументи, які по суті залежать від основної бізнес-логіки тестованої програми.
Синтаксис простий, вам просто потрібно використовувати підкреслення “_” для аргументу, де фактичне значення невідоме.
Наприклад, щоб перевірити будь-яке значення рядка, ви можете просто згадати “_ Як рядок 'Замість аргументу в тесті, і він повинен передаватись для будь-якого значення рядка (аналогічно для інших примітивних, а також користувацьких типів даних).
Давайте розберемося в цьому на прикладі
def 'illustrate mocks for interaction verification with generic matchers'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) 1*studentDatabase.getStudentGrade('123') }
Важливим моментом, який слід зазначити тут, є те, що ви завжди можете поєднувати і поєднувати, які аргументи відомі, а які невідомі. Наприклад, у наведеному нижче прикладі ми перевіряємо взаємодію одного макета з реальними аргументами, а іншого - з вільними збігами.
# 3) Нарешті, давайте розглянемо сценарій, коли ми зможемо встановити порядок макетного виклику, тобто в якому порядку були викликані макети під час виконання тесту.
Іноді дуже важливо перевірити потік подій, коли в тестованому додатку задіяно кілька співавторів / насмішок, і корисно зрозуміти та перевірити, що методи викликались у заздалегідь визначеній послідовності.
def 'illustrate mocks for validating order'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.getStudentGrade('123') then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) }
Цього можна досягти простим використанням декількох блоків 'then:' у порядку очікувань послідовності Mock. Якщо згадана послідовність не відповідає фактичному порядку виклику, виникає помилка із зазначенням 'Неправильний порядок виклику'.
Наприклад, якщо я зміню порядок вищевказаного потім операторів, виконання тесту призведе до помилки, як показано нижче.
Wrong invocation order for: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) (1 invocation) Last invocation: studentDatabase.updateStudentGrade('123', 'C')
Забивання в Спок
Відео-посібник
Ми вивчили все про знущання, а тепер давайте подивимось, як визначити заглушки на знущаних об’єктах. Заглушення - це не що інше, як встановлення заздалегідь визначених або консервованих відповідей на виклики Mock для тестування різних потоків / сценаріїв програми, що тестується.
Подумайте про це як про програму макета для повернення заздалегідь визначеного значення, коли воно було викликане. Ми продовжимо з тим самим додатком StudentGradeCalculator і заглушимо виклики інтерфейсу бази даних, щоб перевірити різні сценарії.
як переглянути файл .dat - -
Заглушка - це як макет, який певним чином імітує поведінку реального об’єкта. Ви можете просто назвати це запрограмованим макетом.
Ступаючий синтаксис
Синтаксис стибінгу - це 2 оператори зсуву вправо - тобто ' >> '
Для того, щоб встановити заглушку для будь-якого дзвінка, ви можете визначити його наступним чином:
StubbedObject.StubbedMethod(//argumentList) >> “Stubbed Response”
Давайте тепер розберемося з різними сценаріями обриву на прикладах.
# 1) Заглушення з реальними параметрами: Якщо аргументи відомі заздалегідь або якщо ви хочете встановити заглушку лише тоді, коли виклик має вказані аргументи, можна використовувати цей спосіб визначення заглушок.
def 'illustrate stubs with exact matchers'() { given: studentDatabase.getStudentScores('123') >> (20F, 30F, 50F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' }
Тут ви можете бачити, що заглушка була встановлена з точним аргументом, тобто StudentId, у цьому випадку як '123' (для будь-якого іншого значення заглушка не буде викликана, і буде повернута відповідь за замовчуванням).
# 2) Забивання м’якими збіжниками: Якщо аргументи невідомі (або не важливі), тоді ми можемо згадувати їх вільно, як це було для макетів, а синтаксис залишається незмінним, тобто підкреслення «_».
def 'illustrate stubs with loose matchers'() { given: studentDatabase.getStudentScores(_ as String) >> (20F, 30F, 10F) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' }
# 3) Давайте подивимось ще один швидкий приклад, коли ми встановлюємо заглушку, щоб створити виняток.
Ці сценарії дуже корисні для перевірки логіки обробки помилок тестованої програми (як у реальному світі, генерувати всі винятки насправді неможливо, але можна створити просту заглушку, щоб повернути будь-яке виключення, яке ми хочемо, а потім затвердити його в тодішній блок).
def 'illustrate stubs with exceptions thrown'() { given: studentDatabase.getStudentScores(_ as String) >> {throw new RuntimeException()} when: studentReportGenerator.calculateStudentGrade('123') then: thrown(RuntimeException.class) }
Шпигунство в Спок
Шпигуни базуються на реальних об'єктах тобто їм потрібна реалізація інтерфейсу, а не сам абстрактний інтерфейс. Шпигуни потужні, і вони можуть дозволити вам отримати реальні методи, що викликаються для програми, що тестується, і перевірити, для яких аргументів були викликані методи.
Шпигуни також дозволяють визначати часткові знущання над екземплярами об'єкта, що підглядається. тобто припустимо, що ви хочете визначити поведінку деяких методів на об’єкті, тоді ви можете дозволити іншим викликати як реальні виклики методів.
Вони, як правило, корисні в ситуації, коли можуть існувати деякі методи інтерфейсу, які не реалізовані, і є небагато інших, які є повністю функціональними. Отже, ви як розробник можете вибрати заглушити нереалізовані та викликати реальні реалізації функціональних методів.
Слід зазначити, що для об'єктів Spied, якщо не визначені заглушки, поведінка за замовчуванням буде викликати реальну реалізацію. Сказавши це, не слід часто телефонувати до шпигунів, і все охоплення сценарію можна досягти, використовуючи макети та заглушки та їх комбінацію.
Давайте подивимось декілька прикладів використання Spies у фреймворці Spock, використовуючи той самий приклад StudentGradeCalculator (Ми створили реальну реалізацію База даних студентів що є реалізацією в пам'яті з використанням HashMap для ілюстрації виклику реальних методів та повернення даних. Код буде доступний в останньому розділі підручника):
# 1) Шпигунство за допомогою комбінації викликів заглушки та реальних методів
def 'illustrate spies'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' 1*spiedStudentDatabase.getStudentGrade(_ as String) >> 'A' }
Наведений приклад ілюструє синтаксис створення Spy за допомогою фреймворка Spock. Заглушка визначається під час оголошення.
Крім того, шпигунські виклики можна перевірити, як показано в блоці then (за допомогою збіжних аргументів, які можна визначити для будь-яких конкретних аргументів).
# 2) Шпигунство за допомогою всіх реальних викликів методів
def 'illustrate spies with real method call'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' 1*spiedStudentDatabase.getStudentGrade('123') }
У наведеному вище прикладі, оскільки ми не згадували жодної пригніченої поведінки, усі виклики будуть спрямовані на реальну реалізацію.
Висновок
У цьому посібнику ми дізналися все про вбудовані прийоми знущання над Stub та Spy за допомогою фреймворка Spock. Spock робить це легким, поєднуючи ці функції як частину самого фреймворку з більш читабельним грузистим синтаксисом разом із меншим типовим кодом.
Макети, заглушки та шпигуни широко використовуються в модульному тестуванні для збільшення охоплення та тестування або перевірки основної бізнес-логіки тестованої програми.
Вихідний код програми
StudentReportGenerator.java - це метод / додаток, що перевіряється
package app.studentScores; import java.util.List; public class StudentReportGenerator { public IStudentDatabase studentDatabase; public StudentReportGenerator(IStudentDatabase studentDatabase) { this.studentDatabase = studentDatabase; } public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; } }
IStudentDatabase.java - Інтерфейс бази даних
package app.studentScores; import java.util.List; public interface IStudentDatabase { List getStudentScores(String studentId); void updateStudentGrade(String studentId, String grade); String getStudentGrade(String studentId); }
StudentDatabase.java - реалізація InMemory інтерфейсу IStudentDatabase.java
package app.studentScores; import java.util.*; public class StudentDatabase implements IStudentDatabase { private Map scoreMap; private Map gradeMap; public StudentDatabase() { this.scoreMap = new HashMap(); this.gradeMap = new HashMap(); scoreMap.put('123', Arrays.asList(40F, 30F, 30F)); scoreMap.put('456', Arrays.asList(10F, 10F, 30F)); gradeMap.put('123', 'C'); gradeMap.put('456', 'A'); } @Override public List getStudentScores(String studentId) { return scoreMap.get(studentId); } @Override public void updateStudentGrade(String studentId, String grade) { gradeMap.put(studentId,grade); } @Override public String getStudentGrade(String studentId) { return gradeMap.get(studentId); } }
У нашому майбутньому підручнику ми побачимо, як інтегрувати фреймворк Spock з іншими тестовими структурами та технологіями.
НАЗАД Підручник | НАСТУПНИЙ підручник
Рекомендована література
- Написання модульних тестів із Spock Framework
- Запитання для інтерв’ю з Spock (найпопулярніші)
- Spock для інтеграції та функціональних випробувань із селеном
- Тестування на основі даних або параметризоване за допомогою Spock Framework
- Підручник з Spock: Тестування за допомогою Spock and Groovy
- Найкращий БЕЗКОШТОВНИЙ підручник з C #: Остаточний посібник для C # для початківців
- Тестування навантаження за допомогою підручників HP LoadRunner
- Функції дати та часу в C ++ з прикладами