spock mocking stubbing
Подигравка, подбиване и шпиониране със Spock:
как да създам списък с обекти в Java
Параметризирано тестване в Spock Framework беше обяснено подробно в това Поредица от учебни уроци по Spock .
Подигравките и сблъсъците са един от най-важните градивни елементи на обширните модулни тестове. Поддръжката за подигравка и подбиване е като черешата на тортата за рамка.
За съществуващи рамки като JUnit, JBehave и др. Поддръжката за макети и мъничета не излиза от кутията, поради което изисква разработчик да използва библиотеки на трети страни като Mockito, PowerMock, EasyMock и др., За да ги използва в единични тестове.
За да разберете макетите и случаите на тяхното използване, можете да разгледате нашата серия от Урок за Mockito .
В този урок ще научим повече за вградените функции за подиграване и забиване, интегрирани в самата библиотека на Spock, което от своя страна би позволило да се използва по-лесният синтаксис на Groovy и по този начин намалява необходимостта от добавяне / включване на други 3rdпартийни библиотеки.
Винаги можете да включите други присмехулни рамки в тестовете си, тъй като всички валидни 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 извикването с предоставените аргументи.
какво е ключът за сигурност за рутера
# две) Сега нека видим как да проверим Mock взаимодействията, без да предоставяме действителните стойности на аргументи, т.е. това, което ни интересува, е просто да знаем, че макетът е бил извикан в метода, но не и с какви аргументи.
Този тип изисквания са най-често срещани, докато се пишат единични тестове за действителния производствен код, тъй като не винаги е лесно да се идентифицират действителните аргументи, които по същество зависят от основната бизнес логика на тестваното приложение.
Синтаксисът е прост, просто трябва да използвате подчертаване „_“ за аргумент, при който действителната стойност не е известна.
Например, за да проверите за стойност на String, можете просто да споменете „_ Като низ ”На мястото на аргумент в теста и той трябва да премине за всяка стойност на String (подобно за други примитивни, както и персонализирани типове данни).
Нека разберем това с пример
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')
Стъпване в Спок
Видеоурок
Проучихме всичко за подигравките, сега нека видим как да дефинираме Stubs върху подиграваните обекти. Пробиването не е нищо друго освен настройка на предварително дефинирани или консервирани отговори на Mock извикванията за тестване на различните потоци / сценарии на тестваното приложение.
Мислете за това като за програмиране на макет за връщане на предварително дефинирана стойност, когато е била извикана. Ще продължим със същото приложение StudentGradeCalculator и ще заглушим извикванията на интерфейса на базата данни, за да тестваме различни сценарии.
Stub е като макет, който в известен смисъл имитира поведението на реалния обект. Можете просто да го наречете като програмиран Mock.
Забиване на синтаксиса
Синтаксисът за поглъщане е 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) Нека да видим още един бърз пример, в който сме настроили мъниче, за да хвърлим изключение.
Тези сценарии са много полезни за валидиране на логиката за обработка на грешки на тествано приложение (както в реалния свят генерирането на всички изключения всъщност не е възможно, но може да се настрои обикновен мъниче, който да върне каквото искаме изключение и след това да го заявим тогавашният блок).
най-добрата програма за отваряне на xml файлове
def 'illustrate stubs with exceptions thrown'() { given: studentDatabase.getStudentScores(_ as String) >> {throw new RuntimeException()} when: studentReportGenerator.calculateStudentGrade('123') then: thrown(RuntimeException.class) }
Шпиониране в Спок
Шпионите се основават на реални обекти т.е. те се нуждаят от изпълнението на интерфейса, а не от самия абстрактен интерфейс. Шпионите са мощни и могат да ви позволят да получите реални методи, извикани за тестваното приложение, и да проверите за какви аргументи са извикани методите.
Шпионите също позволяват да се дефинират частични подигравки върху екземплярите на шпионирания обект. т.е.да предположим, че искате да дефинирате поведението на някои методи на обекта, тогава можете и позволете на останалите да бъдат извикани като реални извиквания на методи.
Те обикновено са полезни в ситуация, в която може да има някои методи за интерфейс, които не са внедрени и има малко други, които са напълно функционални. Следователно вие като разработчик можете да изберете да заглушите неизпълнените и да извикате реалните реализации на функционалните методи.
Трябва да се отбележи, че за шпионираните обекти, освен ако не са дефинирани, поведението по подразбиране ще бъде извикване на реалната реализация. Като каза това, шпиони не трябва да бъдат често извиквани и цялото покритие на сценария може да бъде постигнато с помощта на макети и комбинации от тях.
Нека да видим няколко примера за използване на Spies в рамката на Spock, използвайки същия пример за StudentGradeCalculator (Създадохме истинско изпълнение на StudentDatabase което е реализация в паметта, използвайки 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 framework. Стъблото се дефинира по време на самото деклариране.
Също така, шпионираните повиквания могат да бъдат проверени, както е илюстрирано в тогавашния блок (с разхлабени съвпадения на аргументи, които могат да бъдат дефинирани за всякакви конкретни аргументи).
# 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') }
В горния пример, тъй като не сме споменали никакво потънало поведение, всички обаждания ще отидат за реалното изпълнение.
Заключение
В този урок научихме всичко за вградените техники за Mock Stub и Spy с помощта на Spock framework. Spock го улеснява, като комбинира тези функции като част от самата рамка с по-четлив грууви синтаксис заедно с по-малкия код на шаблона.
Mocks, Stubs и Spies се използват широко в модулно тестване за увеличаване на обхвата и тестване или валидиране на основната бизнес логика на тестваното приложение.
Изходен код за приложението
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 Framework
- Урок за Spock: Тестване със Spock и Groovy
- Най-добрият БЕЗПЛАТЕН урок за C #: Най-доброто ръководство за C # за начинаещи
- Тестване на натоварване с уроци за HP LoadRunner
- Функции за дата и час в C ++ с примери