Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- http://joxi.ru/EA4k7zEUDdq172
- /*
- 1 и 2 - одинаковые куски кода
- ты уже используешь
- tasks = deepcopy(default_tasks)
- так что - и вначале это вполне уместно было бы)
- */
- ************************
- @app.route('/todo/api/v1.0/tasks/default', methods = ['GET'])
- @auth.login_required
- def get_default_tasks():
- /*
- согласна - GET-запрос - тут уместнее
- реализация - тоже ок
- я бы сэекономила на последней строчке)
- реализовала как return get_tasks()
- выигрыш на самом деле небольшой
- но на мой вкус так проще) и чуть более DRY)
- еще - я бы применяла uri = /todo/api/v1.0/tasks/reset
- ну и имя метода из этих же соображений - reset_tasks()
- ресет - точнее отвечает тому что мы делаем
- мы не просто отдаем таски по умолчанию
- мы нашу базу приводим к начальному состоянию
- про то - почему тут Get уместнее
- Нам нужно реализовать в rest-server.py возможность для очистки базы
- У нас же REST сервис, потому нужно следовать “идеологии REST”
- А она говорит - что можно использовать только специальные http methods
- И только по определенному принципу
- Например,
- для создания - использовать post,
- для апдейта - метод put
- и т д
- И нам важно соблюсти этот принцип и свести написание сервисов к одному шаблону
- Вся идея REST была в том, чтобы чтобы потом все могли “общаться на одном языке” и таким образом “понимать друг друга”
- можно было бы реализовать что-то типа такого
- @app.route('/todo/api/v1.0/tasks/reset', methods = [‘PUT’])
- но, хотя мы и делаем “апдейт"
- но - апдейт не ресурса...
- т к reset - это не ресурс
- вот если бы мы для /tasks посылали PUT, то было бы все честно
- а /tasks/reset - это уже не ресурс
- это просто “хак-урл” , чтобы нам выполнить определенное нужное нам действие
- потому в данном случае абсолютно нормально - посылать GET
- наверное, можно и PUT… решай самостоятельно
- еще довод за GET
- обрати внимание - все остальные запросы что-то возвращают - делают return …
- это логично - клиент послал запрос, нужно ему что-то вернуть)
- и тут было бы логично вернуть все таски = (то же, что возвращает запрос get)
- это важный довод - почему стоит использовать именно GET для нашего запроса
- */
- ***************************************
- http://joxi.ru/8An6LoZhqBxJ0A
- /*
- Вот еще источник возможных проблем
- если добавлять первую таску - то будут проблемы
- https://lancelote.gitbooks.io/intermediate-python/content/book/ternary_operators.html
- https://lancelote.gitbooks.io/intermediate-python/content/book/comprehensions.html (dict абстракции)
- http://www.diveintopython.net/native_data_types/index.html#odbchelper.dict
- */
- ************************************
- public class RESTfullTextsTest {
- /*
- чуть подправить - чтоб по конвеншенсам - CamelCase
- https://google.github.io/styleguide/javaguide.html#s5.3-camel-case
- и термин = RESTful API
- одна буковка у тебя лишняя)
- получим - RestFulTasksTest
- */
- *********************************
- src / main / java / com / tasj / entities /
- /*
- я бы использовала термин containers для этого пекеджа
- и класс ErrorContainer - тоже сюда переноси
- того же плана класс
- кстати, никаким классам-контейнерам, кроме класса Task - и не надо реализовывать
- ни конструкторов, ни equals, ни hashCode
- может - equals и hashCode - для ErrorContainer
- и то, как по мне - не очень нужно
- кстати - для классов тех объектов, которые будешь сравнивать
- реализуй toString - чтоб в случае чего - сообщения об ошибке были наглядными
- */
- *****************************
- public static final String URI = "http://localhost:5000/todo/api/v1.0/tasks";
- /*
- Тут есть решение более настраиваемое
- final - нам не очень подходит
- вот эта часть - http://localhost:5000/ - зависит от того - где наш рест-сервер задеплоен
- остальная часть - это уже скойство нашего рест сервера
- вот в Сonfiguration.baseUri - сохраним http://localhost:5000 = домен
- чтобы его можно было от случая к случаю реконфигурить...
- Ведь это как раз та часть урла - которая не принадлежит ресурсу,
- и зависит от того на каком сервере/по-какому-адресу ресурс/веб-сервис задеплоен
- На самом деле здесь есть несколько способов как лучше сделать
- Первый, если ты уверена, что тебе не нужно будет одновременно/параллельно посылать запросы на разные урлы
- То тогда можно использовать Configuration.baseUrl типа обычная глобальная переменная
- если нет, тогда лучше "ооп" подход, когда обьект-ресурс помнит свой базовый урл (доменную часть)
- который передается при создании через конструктор
- нам модульный тут вполне подходит
- а полный uri - уже собирай внутри класса-ресурса
- */
- ****************************
- public static Invocation.Builder authorized(Invocation.Builder request){
- return request.header("Authorization", "Basic " + DatatypeConverter.printBase64Binary("miguel:python".getBytes()));
- }
- /*
- не зашивай внутрь метода вот это "miguel:python"
- передавай как параметр эту строчку
- так метод будет абсолютно универсальным
- а уже внутри класса-ресурса - реализуешь свой authorizedRequest(String uri)
- в котором вызовешь Connections#authorized
- и передашь туда credentials = "miguel:python"
- */
- ********************************
- public class Connections {
- /*
- подумай над названием RestHelpers )
- потенциально - может немного разрастись)
- не ограничивай себя именем класса раньше времени)
- */
- ************************
- public class RestResources {
- /*
- Это наш класс-ресурс
- По аналогии с пейджами, класс-ресурс выносим в пекедж - resources.
- Поскольку мы работаем только с тасками - в имена методов-действий можно не включать слово Tasks
- Имена методов-проверок - по-прежнему указываем точно
- А этот класс - можно было бы назвать Tasks ( т к ресурс-модуль, а не ресурс-объект = TasksResource)
- И в коде потом можно без статического импорта писать
- Response response = Tasks.create(URI, "give lesson");
- Это было бы и наглядно, и на будущее - на случай, если в тесте появятся запросы к другим ресурсам
- Варианты имени для класса-ресурса
- Tasks - неплохое имя
- но у нас есть класс Task
- и можно сказать - что мі используем один термин для достаточно разных понятий….
- Можно в таком случае для класса-ресурса использовать TasksApi или TasksRest в таком случае…
- Но и Tasks - тоже неплохо…, но хуже)
- Можно пойти на эту уступку в принципе - код понятный, названия наглядные
- На реальном проекте обычно создают класс-ресурс (объект или модуль) не сразу, когда начали писать тесты.
- А позже - когда появится необходимость переиспользовать вспомогательные методы в разных тест-классах.
- Или если одни более квалифицированные члены команды разрабатывают класс-ресурс, а другие - используют для разработки тестов.
- Но или если так принято в данной команде)
- Т е - это делается обычно при необходимости. С пейджами - в общем аналогично.
- */
- **************************************
- public static Response getRequest(){
- public static Response getDefaultRequest(){
- public static Response putRequest(int taskId, Task task){
- public static Response postRequest(Task task){
- public static Response deleteRequest(int taskId){
- /*
- можно лаконичнее называть методы
- get
- reset
- create / post
- update / post
- delete
- Правильный статус ответа на каждый из запросов - строго определенный
- И если мы будем проверять статут ответа в тест-методе - то надо об этом думать и вспоминать - какой статус в каких случаях верный
- И можно выкрутиться - проверять статус ответа в нутри степа, а возвращать собственно entity из запроса.
- Сразу пара потенциально полезных моментов)
- Если встроить проверку response кода прямо в сам степ
- Тут сразу появляется интересный нюанс
- Дело в том, что мы так спрячем тестовую логику внутрь степа, что не очень хорошо
- С другой стороны - эта проверка = само собой разумеющееся
- Эта проверка достаточно стандартная и всегда будет повторяться после вызова степа (если будет жить вне степа)
- А раз так - то почему бы и не спрятать?
- На самом деле - нет однозначного ответа на этот вопрос...
- Так - со спрятанной проверкой - код станет проще во всех тестах - но станет до некоторой степени “магическим”
- Тут надо решать самостоятельно - как лучше )
- На твое усмотрение
- Еще момент
- В этих API тестах - самих тестов может быть не так уж и много -
- чтоб еще и заморачиваться над DRY кода...нужно и этот аспект учитывать
- Но если пойти этим путем = прятать в степе проверку статуса response, то код будет намного более удобным:
- @Test
- public void testCreate() {
- Response response = Tasks.create("give lesson");
- assertEquals(201, response.getStatus());
- assertEquals("give lesson", titleFor(response));
- }
- vs
- @Test
- public void testCreate() {
- Task task = Tasks.create("give lesson");
- assertEquals("give lesson", task.getTitle());
- }
- */
- *********************************
- @Test
- public void testCreate() {
- Response response = postRequest(new Task("task title"));
- assertEquals(201, response.getStatus());
- assertEquals("task title", getTaskFrom(response).getTitle());
- /*
- если и проверять таску из ответа - то уже используй
- assertEquals(new Task(....), getTaskFrom(response))
- хотя - решение - проверять таску из ответа или нет - надо отдельно проверять
- если это уже на каком-то более низком уровне проверяется
- то тут - только проверили статус ответа
- и важное = состояние списка тасок (всего списка) = функциональную проверку делаем
- */
- List<Task> receivedTasks = getTasksFrom(getRequest());
- assertEquals(3, receivedTasks.size());
- /*
- это нужно уточнить - првоерять не только количество тасок
- а вообще все таски
- assertEquals - для сравнения списков тасок - вполне ок будут работать - т к в классе Task -
- реализованы equals & hashcode
- разумно реализовать в ресурсе - assertTasks(Task... tasks)
- и внутри метода - получили гет-запросом таски
- и сравнили с переданными с метод
- и тебе бы больше пригодилось - если бы тестовые данные были массивом, а не списком
- причем - final-массивом
- дочитай ревью - думаю - сообразишь - как final-массив применять
- */
- }
- *********************************
- putRequest(int taskId, Task task)
- /*
- а не надо ид передавать отдельно и потом урл собирать
- ведь у нас uri в Task task - уже содержится
- */
- ***********************************
- deleteRequest(int taskId)
- /*
- а тут передавать ид - вполне уместно и экономно
- т к кроме ид - и не надо ничего
- */
- ***************************************
- @Test
- public void testUpdate() {
- List<Task> expectedTasks = new ArrayList<Task>();
- expectedTasks.addAll(defaultTasks);
- expectedTasks.get(0).setTitle("updated title");
- Response response = getRequest();
- Task task = getTasksFrom(response).get(0);
- task.setTitle("updated title");
- Response putResponse = putRequest(1, task);
- assertEquals(expectedTasks.get(0), getTaskFrom(putResponse));
- List<Task> receivedTasks = getTasksFrom(getRequest());
- assertEquals(expectedTasks, receivedTasks);
- }
- /*
- сложновато получилось
- делаем новый список из тасок по умолчанию
- меняем таске что-то там
- делаем гет-запрос
- меняем таске из респонса тайтл
- только потом делаем put
- потом сравниваем списки
- а можно и вот так
- */
- @Test
- public void testUpdate() {
- Task task = new Task("t2 title", "t2 description", true, TasksApi.uri + "/2");
- TasksApi.update(task);
- TasksApi.assertTasks(DEFAULT_TASKS[0], task);
- }
- /*
- ну, если с респонсом работать в тест-методе -
- то, конечно, чуть сложнее
- но все равно попроще, чем сейчас
- */
- *************************************
- /*
- я тесты запускала
- когда запускаешь только testDelete() - все ок
- а вот когда все тесты - проблемы
- когда реализуешь toString() для класса Task - сообщения об ошибках будут нагляднее
- а если закомментить тесты с редактированием таски
- то все ок будет работать)
- так что - ищи корни проблем там
- */
Add Comment
Please, Sign In to add comment