julia_v_iluhina

Untitled

Jul 24th, 2016
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 24.85 KB | None | 0 0
  1. http://joxi.ru/nAyqEx7HX63q9A
  2. красиво придумал поправить метод создания тасок)
  3.  
  4.  
  5. http://joxi.ru/Dr860ybhkgybn2
  6.  
  7. Ну, технически - можно и так)
  8.  
  9. Только тут у нас на самом деле есть варианты
  10.  
  11. условно назовем вариант delete all
  12. (тобой реализованный вариант)
  13. когда предварительные действия в тест-метода =  delete all + add some tasks
  14.  
  15. и вариант reset = когда при его вызове - снова получаем те же 2 таски, что были на начало теста...
  16.  
  17. Delete all
  18. В такой реализации - код удаления всех тасок без каких-то сложностей особенных.
  19. Это плюс. А минус в том, что для гивен-методов тогда надо реализовывать дополнительное что-то.
  20. На начало тест-метода часто нужны существующие таски - нам же надо реализовать тесты для апдейта или удаления.
  21.  
  22. Судя по реализации гивенов - когда в гивенах создаются таски -  то используется сначала delete all,
  23. а потом добавление тасок.
  24. Т е действий - больше, чем если бы мы реализовали reset.
  25.  
  26. Если deleteAll or  add отвалились - то гивены не работают.
  27. Хотя, с другой стороны, если reset отвалился - тоже беда)))
  28. Но, в таком случае, все же это меньшее зло,
  29. т к этот reset служит только для тестовых целей
  30. и с развитием сервера он вряд ли будет меняться,
  31. да и логика у него и у так реализованных гивенов - значительно проще...
  32.  
  33. Более того, с reset получше вероятности по надежности гивенов -
  34. с reset только одно звено (примитивное),
  35. а с delete all - два (реально использующихся в рабочих целях = более сложных = add + delete all)
  36.  
  37. Еще такой момент.
  38. При тестировании операции add - вызывать гивен, в котором уже вызван add - как-то странно)
  39. Т е при таких раскладах - надо вызывать гивен строго в ситуации, кгда тасок мы не добавляем
  40. Это конечно не KISS тоже - то, что надо помнить о каких-то ограничениях
  41.  
  42. Из этой же серии
  43. delete all - тоже операция, которая может быть востребованой в работе приложения
  44. Значит - и ее надо тестировать)
  45. А раз так - для delete all вызываем гивен, в котором вызываем delete all
  46. Тут уже идея с delete all в качестве вспомогательной операции - заходит в тупик)  
  47.  
  48. С Delete all хорошо то, что гивены гибкие и можно более интересную тестовую ситуацию создать.
  49. правда, вопрос - кому она тут нужна)))
  50. т к тут, на этом этапе - достаточно простые вещи нам нужно проверить
  51. супер-навороченых гивенов в общем-то и не надо
  52.  
  53.  
  54. вывод -  =  с reset = 2 таски по дефолту более KISS получается)
  55. если бы стояла задача в REST API тестах прогнать работу с тасками  -
  56. у которых тексты разной длины/кодировки ....,  
  57. то может - навороченные гивены, использующие delete all, были бы и нужны.
  58. да и то - не факт )
  59. И вот это очень серьезное замечание - когда delete all тестируется используя в предварительніх действиях саму себя
  60. Я - сторонник варианта с reset - как варианта, в котором меньшее количество вещей может сломаться
  61. и как более быстрого и более однозначного варианта
  62.  
  63. так что - лучше избавься от delete all и от сложных гивен-методов
  64. и реализуй reset. И далее - перед каждым тест-методом просто его используй - всегда получишь одинаковую начальную тестовую ситуацию.
  65.  
  66. Нужно грамотно воспользоваться тем, что уже есть в коде
  67. Если начальное состояние фиксировать в некой коллекции tasks_default,
  68. а при старте и ресете - копировать в  tasks содержимое tasks_default
  69. В python есть способ  - скопировать содержимое одного списка в другой с помощью одной операции,
  70. а не переносить каждый элемент списка отдельно
  71.  
  72. Полезные линки
  73. http://www.python-course.eu/python3_global_vs_local_variables.php
  74. https://docs.python.org/2/library/copy.html
  75. http://joxi.ru/Dr8vxzPFk3Kl32
  76. http://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list-in-python
  77. http://joxi.ru/J2b9KdBt41qEpm
  78. *************************************************************  
  79. http://joxi.ru/p275M9zs0q5Gqr
  80.  
  81. Технически - класс Task - не контейнер
  82. но в общем - конструкция некоторых контейнеров
  83. на этих основаниях можно его в пекедже containers разместить
  84. возле тест-класса - совсем ни к чему он )
  85.  
  86. **************************************************************
  87. public class Task {
  88.     private int id;
  89.     private String title;
  90.     private boolean done;
  91.     private String description;
  92.     private String uri;
  93.  
  94. /*
  95.     посмотри - что нам приходит в качестве тасок
  96.     (через System.out.println(.....readEntity(String.class));
  97.    
  98.     получаем
  99.       {
  100.         "tasks": [
  101.           {
  102.             "description": "Milk, Cheese, Pizza, Fruit, Tylenol",
  103.             "done": false,
  104.             "title": "Buy groceries",
  105.             "uri": "http://localhost:5000/todo/api/v1.0/tasks/1"
  106.           },
  107.           {
  108.             "description": "Need to find a good Python tutorial on the web",
  109.             "done": false,
  110.             "title": "Learn Python",
  111.             "uri": "http://localhost:5000/todo/api/v1.0/tasks/2"
  112.           }
  113.         ]
  114.       }
  115.       т е  -  id - не получишь)
  116.       в статье про этот момент тоже написано
  117.       https://habrahabr.ru/post/246699/ раздел Улучшаем интерфейс нашего сервиса
  118.      
  119.       избавляйся от id)
  120.      
  121.      
  122.       еще по конструкторам
  123.       реально - ты далеко не все конструкторы используешь
  124.       оставь конструкторы
  125.       без параметров
  126.       с заполнением title (тут - остальные поля заполни по умолчанию,
  127.       ну - или сделай это - когда увидишь проблемы со сравнением тасок)
  128.       c заполнением всех реквизитов
  129.      
  130.       конструктор с заполнением title - нам будет нужен для теста создания таски, которой мы заполняем только title
  131. */  
  132.  
  133.     public static Response create(Task task) {
  134.     public static Response update(int taskID, Task task) {
  135.     public static Response delete(Task task) {
  136.     public static Task get(Response response) {
  137.     ...
  138. /*
  139.     Ну... буду ругаться )))
  140.     ты же уже в курсе про принцип Single Responsibility)
  141.    
  142.     Мы ж пока фреймворк для работы на селениуме делали - про это мноого говорили
  143.    
  144.     у тебя получилась смесь бульдога с носорогом)
  145.    
  146.     с одной стороны - структура для контейнеров
  147.     а с другой - класс-ресурс
  148.    
  149.     давай ты структуру для контейнеров оставишь именно этим
  150.     а в класс-ресурс вынесешь то, что относится именно к нему
  151.    
  152.     ниже - пояснения
  153. */
  154.      
  155. /*
  156. Вспомогательные методы разумно вынести в аналог пейджа.
  157.   Теперь у нас будет пекедж не pages, a resources.  А правила работы с таким классом - аналогичные.
  158.  
  159.   Поскольку мы работаем только с тасками - в имена методов-действий можно не включать слово Tasks
  160.   Имена методов-проверок - по-прежнему указываем точно
  161.  
  162.   Логично было бы назвать класс Tasks (будем реализовывать как ресурс-модуль, а не ресурс-объект)
  163.   И в коде потом можно без статического импорта писать
  164.           Response response = Tasks.create(URI, "give lesson");
  165.   Это было бы и наглядно, и на будущее - на случай, если в тесте появятся запросы к другим ресурсам
  166.  
  167.   (URI - тоже можно было бы разместить в классе-ресурсе. Потом и это подрихтуем немного)
  168.  
  169. Варианты имени для класса-ресурса
  170.   Tasks - неплохое имя
  171.   но у нас есть класс Task
  172.   и можно сказать - что мы используем один термин для достаточно разных понятий….
  173.  
  174.   Можно в таком случае для класса-ресурса использовать TasksApi или  TasksRest в таком случае…
  175.  
  176.   Но и Tasks - тоже неплохо…, но хуже)
  177.   Можно пойти на эту уступку в принципе - код понятный, названия наглядные
  178.  
  179.  
  180. Когда на реальном проекте обычно создают класс-ресурс (объект или модуль)
  181.   На реальном проекте обычно создают класс-ресурс (объект или модуль) не сразу, когда начали писать тесты.
  182.   А позже - когда появится необходимость переиспользовать вспомогательные методы в разных тест-классах.
  183.   Или если одни более квалифицированные члены команды разрабатывают класс-ресурс,
  184.   а другие - используют для разработки тестов. Но или если так принято в данной команде)
  185.   Т е - это делается обычно при необходимости. С пейджами - в общем аналогично.
  186.  
  187. */      
  188.    
  189.     public static boolean assertTasks(Task task, Task task1) {  
  190.     public static boolean assertTitles(Task task, Task task1) {
  191.     public static boolean assertDescription(Task task, Task task1) {
  192.     public static boolean assertDone(Task task, Task task1) {
  193. /*
  194.     ассертам тоде не место в классе Task
  195.     сами ассерты будем серьезно дорабатывать)
  196. */
  197.      
  198. /*
  199.     аналогично - по другим классам-контейнерам
  200.     пусть останутся только контейнерами)
  201. */      
  202. **********************************************
  203.  
  204.   public class Helper {
  205.   /*
  206.     точнее было бы RestHelpers
  207.     херперЫ для REST-а
  208.     тут - место для универсальных методов
  209.     никак не привязанных с особенностями конкретного приложения
  210.   */
  211.  
  212.     public static Invocation.Builder requestTo(String uri) {
  213.     public static Invocation.Builder authorized(String login, String password, Invocation.Builder requestBuilder) {
  214. /*
  215.     эти 2 метода - действительно универсальные методы, методы для RestHelpers
  216. */
  217.     public static Invocation.Builder authorized(Invocation.Builder requestBuilder) {
  218.         return authorized(TestData.LOGIN, TestData.PASSWORD, requestBuilder);
  219.     }
  220. /*
  221.     А это - уже не здесь)
  222.     это уже стоит реализовывать в классе-ресурсе
  223.    
  224.     насчет логина-пароля и их производых
  225.     этот сервис подразумевает каких-то других юзеров, кроме miguel?
  226.     если бы да - то я согласилась бы с тобой, что стоило бы реализовать TestData
  227.     в котором есть информация о юзерах
  228.     вывод - нам TestData не нужен
  229.     достаточно будет в классе-ресурсе в реализации authorized - зашить "miguel:python"
  230. */
  231.     public static Response givenTasks(Task... tasks) {
  232.     public static void givenEmptyList() {
  233.  /*
  234.     из написанного выше - не нужны тебе гивен-методы
  235.     это раз
  236.     и им точно не место в RestHelpers
  237.     если бы и были они нужны - то реализовывать их стоило в классе-ресурсе    
  238.  */
  239. **********************************************  
  240.  
  241. /*
  242.     Что же там (в классе-ресурсе) у нас будет
  243.    
  244.     наш URI "http://localhost:5000/todo/api/v1.0/tasks" - как раз URI для ресурса tasks
  245.     значит - тут и разместим
  246.     на самом деле - и с URI можно еще кое-что улучшить
  247.     но пока не будем распыляться - сделаем при следующих ревью
  248.    
  249.     практически для всех запросов к серверу нам нужно быть авторизированными
  250.     поскольку только в рамках этого класса мы реализуем методы, которые будут делать такие запросы
  251.     то тут и разместим метод, который будет делать авторизированный запрос для "miguel:python"
  252.     воспользуйся уже реализованным универсальным методом, передав туда в качестве параметра "miguel:python"
  253.     подумай, какой параметр должен быть у метода и что он должен возвращать
  254.    
  255.     а дальше - реализуем надор методов, работающих с ресурсом
  256.     поскольку работаем с тасками - уже в именах методов-действий это не уточняем
  257.     методы-действия
  258.       - получить таски
  259.       - создать таску (2 метода - только по заголовку или по всей информации)
  260.       - обновить таску
  261.       - удалить таску
  262.      
  263.       - выполнить reset
  264.      
  265.     метод-проверка
  266.       - проверить таски (нам нужен такой метод, который будет сверять ожидаемый результат с фактическим - для всех тасок)
  267.      
  268.   Есть интересный момент
  269.       Встраивать ли проверку статуса в метод-действие
  270.       Если встроить проверку response кода прямо в сам степ
  271.      
  272.       Дело в том, что если так делать - то мы спрячем тестовую логику внутрь степа, что не очень хорошо
  273.       С другой стороны - эта проверка = само собой разумеющееся
  274.       Эта проверка достаточно стандартная и всегда будет повторяться после вызова степа (если будет жить вне степа)
  275.       А раз так - то почему бы и не спрятать?
  276.       На самом деле - нет однозначного ответа на этот вопрос...
  277.       Так - со спрятанной проверкой - код станет проще во всех тестах - но код станет до некоторой степени “магическим”
  278.       Тут надо решать самостоятельно - как лучше )
  279.       На твое усмотрение
  280.      
  281.       Еще момент
  282.       В этих API тестах - самих тестов может быть не так уж и много -
  283.       чтоб еще и заморачиваться над DRY кода...нужно и этот аспект учитывать
  284.       Но если пойти этим путем = прятать в степе проверку статуса response, то код будет намного более удобным:)
  285.      
  286.       Этот момент также повлияет на то, что каждый из методов нам будет возвращать
  287.      
  288. */
  289.  ****************************************
  290.  
  291. public class RestfulTasksTest {
  292. /*
  293.     по правилам  CamelCase
  294.     RestFulTasksTest
  295.     https://google.github.io/styleguide/javaguide.html#s5.3-camel-case
  296. */  
  297.  
  298.     public static final String URI = "http://localhost:5000/todo/api/v1.0/tasks";
  299.   /*
  300.     это уйдет в класс-ресурс - т к URI именно для тасок
  301.   */
  302.  
  303.     Task task1 = new Task(1, "task1", false, "desc1", URI + "/1");
  304.     Task task2 = new Task(2, "task2", false, "desc2", URI + "/2");
  305.   /*
  306.     Вместо этого, здесь же - с учетом реализации ресета
  307.     объяви таски по умолчанию
  308.    
  309.       С таким вынесением тестовых данных в переменные надо быть осторожным
  310.       Важно - не надо тестовые данные объявлять в ресурс модуле (опять аналогия с пейджом - мы и там такого правила придерживались)
  311.       Тут и правда есть смысл объявить эти данные
  312.       Часто тестовые данные разумно оставлять в логике самих тестов - таким образом код тестов более очевидный. Пример - тудуэмвиси тесты
  313.       Но - если эти данные вынесены в переменные - то их и в таких случаях стоит использовать
  314.   */
  315.  
  316.     @Test
  317.     public void testUnauthorizedError() {
  318.         Response response = requestTo(URI).get();
  319.  
  320.         assertEquals(403, response.getStatus());
  321.         assertEquals(new ErrorContainer("Unauthorized access"), response.readEntity(ErrorContainer.class));
  322.         /*
  323.             стоило ли реализовывать equals для класса ErrorContainer - вопрос)
  324.             ведь только тут это пригодилось
  325.             и вот такой код сравни            
  326.              assertEquals("Unauthorized access", response.readEntity(ErrorContainer.class).getError());
  327.             по-моему, он попроще таже будет)
  328.         */
  329.     }
  330.  
  331.     @Test
  332.     public void testReadTasks() {
  333.         givenTasks(task1, task2);
  334.  
  335.         List<Task> receivedTasks = TasksContainer.getReceivedTasks();
  336.         /*
  337.             getReceivedTasks - реализуем в классе-ресурсе
  338.             и назовем его просто get
  339.             TasksAPI.get() - вполне понятно, о чем
  340.            
  341.             согласна, прикольно, когда метод get вернет нам сразу список тасок
  342.            
  343.             но тогда в рамках метода стоит проверить статус (выше было про это)
  344.         */
  345.         assertTrue(assertTasks(task1, receivedTasks.get(0)));
  346.         assertTrue(assertTasks(task2, receivedTasks.get(1)));
  347.         /*
  348.             про проверки
  349.             вспомни - как ты проверял состояние тасок в тудуэмвиси
  350.            
  351.             ты сравнивал состояние ВСЕХ тасок - фактические и ожидаемые
  352.            
  353.             мы можем получить такого плана код
  354.                 public static void assertTasks(Task... expectedTasks) {
  355.                         List<Task> actualTasks = get();
  356.                         assertEquals(Arrays.asList(expectedTasks), actualTasks);
  357.                 }
  358.             в метод передаешь ожидаемые таски
  359.             в методе - получаешь запросом фактические таски и сравниваешь их
  360.            
  361.             А если реализуешь метод toString для класса Task - и сообщение об ошибке будет более информативным.
  362.            
  363.             метод - для класса-ресурса
  364.            
  365.             посмотри полезные линки (ты уже многое и реализовал для такого сравнения)
  366.               http://www.java2s.com/Tutorial/Java/0140__Collections/CheckingforEquality.htm
  367.               http://www.javapractices.com/topic/TopicAction.do?Id=17
  368.               http://javadevnotes.com/java-array-to-list-examples
  369.              
  370.        */
  371.     }
  372.  
  373.     @Test
  374.     public void testCreate() {
  375.         /*
  376.             предыдущий метод называется testReadTasks()
  377.             этот testCreate()
  378.            
  379.             думаю - про Tasks нужно уточнять в каждои имени метода или вообще не уточнять
  380.             т к мі можем работать только с тасками
  381.            
  382.             мы реализуем 2 метода для создания таски
  383.             1 - для создания, когда мы задаем лишь title таски
  384.             2 - когда полную информацию
  385.         */
  386.         givenTasks(task1);
  387.         Response response = Task.create(task2);
  388.         /*
  389.             метод create - реализуй внутри класса-ресурса
  390.             он, кстати, по аналогии с методом get - может возвращать таску
  391.             и внутри него - тоде можно проверить статус респонса
  392.             (сейчас статус ты не всегда проверяешь)
  393.         */
  394.  
  395.         Task taskFromResponse = Task.get(response);
  396.         assertEquals(201, response.getStatus());
  397.         assertTrue(assertTasks(task2, taskFromResponse));
  398.  
  399.         List<Task> tasks = TasksContainer.getReceivedTasks();
  400.         assertEquals(2, tasks.size());
  401.         assertTrue(assertTasks(task1, tasks.get(0)));
  402.         assertTrue(assertTasks(task2, tasks.get(1)));
  403.         /*
  404.             тут, вместо 2-ух этих блоком - достаточно воспользоваться реализованным assertTasks
  405.            
  406.             по такой же схеме и следующие тест-методы строй
  407.         */
Advertisement
Add Comment
Please, Sign In to add comment