Оценка затрат на разработку через TDD

в 16:49, , рубрики: tdd, оценка времени, простые принципы, управление разработкой

Скромная дискуссия по мотивам моей вчерашней публикации на тему прогнозирования времени на разработку, в очередной раз пробудила во мне ощущение некоей неправильности на тему использования чисто умозрительного подхода к разбиению истории на задачи. На мой взгляд, когда мы пишем задачи в списке, даже когда мы используем объектную или функциональную терминологию, мы не вполне представляем себе все модули с кодом, которые нам потребуется разработать или доработать.

Тогда мне пришла в голову идея, после разбиения пользовательской истории на задачи, попробовать набросать черновики модульных тестов, для классов или методов, которые я упоминаю в задачах. Мне даже не нужно выдумывать пользовательскую историю, я могу взять одну из моей текущей работы. Например:

Как система, я хочу отправить на проверку KYC сервису персональные данные пользователя

И список задач к ней:

  1. Реализовать получение персональных данных пользователя из локальной базы
  2. Реализовать создание папки в KYC сервисе с указанием в метаданных папки персональных данных пользователя
  3. Реализовать создание запроса на проверку данных папки в KYC сервисе
  4. Реализовать сохранение информации о запросе на проверку персональных данных в локальной базе

Если за продолжительность одной итерации команды считать две недели, то один-два дня вполне можно потратить на ее планирование, но не в виде совещания с умозрительными рассуждениями и покером, а в основном в виде обычной программистской деятельности в виде написания тестов в некоем минимальном виде. С последующей оценкой времени на реализацию функциональности уже в полном масштабе.

В первом приближении выглядит как со всех сторон профит. Пишем код и не теряем времени на коллективные рассуждения. И строим оценки на основе хотя бы минимального количества кода, пощупав так сказать требования заказчика руками.

Поскольку для первой задачи из вышеозначенного списка у меня есть уже оттестированная рабочая реализация, то перехожу ко второй задаче и начинаю делать черновик теста для метода, выгружающего данные в KYC сервис:

describe('upload personal info', () => {

  const HttpFake = require('HttpFake');
  const http = new HttpFake();
  const store = require('../../src/store');
  const handler = require('../../src/kyc/createFolder');
  const rules = require('../../src/importRules');
  const httpOptions = {
    hostname: 'http://kycservice',
    port: 80,
    path: '/api/folder',
    body: {
      meta_data: {
        firstName: 'Jack',
        lastName: 'Sparrow',
        birthDate: '06/01/1740'
      }
    },
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    }
  };
  const httpResponse = {
    statusCode: 200,
    body: {
      folder_id: '0x382974',
      meta_data: {
        firstName: 'Jack',
        lastName: 'Sparrow',
        birthDate: '06/01/1740'
      }
    },
    headers: {
        'Content-Type': 'application/json'
    }
  };

  let context = null,
      handle = null;

  beforeEach(() => {
    context = {
      http,
      store,
      rules
    };

    handle = handler.bind(context, ['folder']);

    const person = {
      firstName: 'Jack',
      lastName: 'Sparrow',
      birthDate: '06/01/1740'
    };
    store.dispatch({ type: 'PERSON', person });
  });

  it('should assign store folder state value', () => {

    http.expect(httpOptions);
    http.returns(httpResponse);

    const assert = checkExportResult.bind(context, [done]);
    store.subscribe(assert);

    handle();
  });

  function checkExportResult(args){

    const folder = store.getState().folder;

    if(folder === null)
      return;

    expect(folder.folder_id).toEqual('0x382974');
    expect(folder.meta_data.firstName).toEqual('Jack');
    expect(folder.meta_data.lastName).toEqual('Sparrow');
    expect(folder.meta_data.birthDate).toEqual('06/01/1740');

    const checkIsCompleted = args[0];
    checkIsCompleted();
  }
});

Модульный тест второй задачи разложился аж в 100 строчек кода, и на его реализацию у меня ушло около часа времени. При этом первые пять строчек на некоторое время погрузили меня в размышления на тему "как бы мне реализовать имитацию отправки http запросов?", пока я не вспомнил что у меня уже есть некая псевдо-http реализация, имитирующая как раз отправку нужных мне POST запросов. Хорошо. Вот у меня есть тест. Стало ли мне понятнее сколько может потребоваться времени на реализацию нужной функциональности?

На самом деле да. Эта задача на мой взгляд очень похожа на задачу с запросом данных из базы, и я уже принял решение что одного рабочего дня мне вполне хватит чтобы реализовать в таком же подходе отправку POST запроса с сохранением результатов в контейнер состояния. А поскольку у меня уже есть успешный опыт с отдельной реализацией правил обработки данных в рамках выполнения запроса, то я решил что реализую новый набор правил, которые я буду использовать при отправке POST запросов.

Две последующие задачи из списка кажутся мне очень похожими, так что писать тесты для них прямо сейчас я не вижу особого смысла, потому что мозг уже принял в качестве оценки:

  1. Реализовать получение персональных данных пользователя из локальной базы
  2. Реализовать создание папки в KYC сервисе с указанием в метаданных папки персональных данных пользователя —
  3. Реализовать создание запроса на проверку данных папки в KYC сервисе —
  4. Реализовать сохранение информации о запросе на проверку персональных данных в локальной базе —

Ну и поскольку следующие пользовательские истории в списке у меня из этой же оперы, просто в POST запросах будет не JSON, а бинарные данные, то я автоматически принимаю что могу за двухнедельную итерацию реализовать три пользовательских истории.

Автор: SergeyEgorov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js