SlideShare a Scribd company logo
1 of 96
Download to read offline
Тесты на поведение
     супротив
тестов на состояние

     Бибичев Андрей
      декабрь 2011
@bibigine
                     Андрей Бибичев

• E-mail:       bibigine@gmail.com
• Twitter:      @bibigine
• Profile:      http://tinyurl.com/bibigine
• Slideshare:   http://www.slideshare.net/bibigine
Для кого и зачем?
• Beginner
 o хорошая точка входа


• Intermediate
 o поможет лучше всё структурировать в голове и
   объяснять коллегам


• Advanced
 o можно использовать для обучения и проверки
   других
Min  Max
                    Кол-
             День          Cycle Cycle
                     во
                           Time Time
              1      0      -      -
        N     2      0      -      -
              3      2      3      3
              4      3      3      4
              5      1      4      4




Cycle Time
Контрольный пример:

                            Min  Max
                    Кол-
             День          Cycle Cycle
                     во
                           Time Time
              1      0      -      -
              2      0      -      -
         5    3      0      -      -
              4      0      -      -
              5      1      5      5
Conveyor

tick(Item[*]):Item[*]

             workers
         *
       Worker                         Item




                       queue
  enqueue(Item[*])             * lifeTime():int
  tick():Item[*]                tick()
Conveyor




 Item




Worker
Conveyor

tick(Item[*]):Item[*]

             workers
         *
       Worker                         Item




                       queue
  enqueue(Item[*])             * lifeTime():int
  tick():Item[*]                tick()
У вновь созданной детали
время жизни должно равняться нулю


           дано: новая деталь,
 когда: запрашиваем у нее время жизни,
     тогда: получаем в результате 0
import org.junit.Test;
import static org.fest.assertions.Assertions.assertThat;

public class ItemTest {
    @Test
    public void shouldHaveZeroLifeTimeAterCreation() {
        // given
        final Item item = new Item();
        // when
        int lifeTime = item.lifeTime();
        // then
        assertThat(lifeTime).isZero();
    }




                                                 FEST
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FluentAssertions;

[TestClass]
public class ItemTest
{
    [TestMethod]
    public void ShouldHaveZeroLifeTimeAterCreation()
    {
        // given
        var item = new Item();
        // when
        var lifeTime = item.LifeTime;
        // then
        lifeTime.Should().Be(0);
    }

                                     FluentAssertions
Это «вырожденный»
 тест на состояние
public class Item {
    public int lifeTime() {
        return 0;
    }
}




public class Item
{
    public int LifeTime { get; }
}
Дано:

   арбуз + гиря 1 кг = гиря 6 кг
   арбуз = ?


Решение:

        x+1=6
        x=6–1=5


Ответ: 5 кг
vs.
Test…                    Should…
{                        {
    // Arrange               // GIVEN
    …                        …
    // Action                // WHEN
    …                        …
    // Assertion             // THEN
    …                        …
}                        }
Март 2006
Журнал «Better Software»
Статья «Introducing BDD»
       Dan North
Альтернатива
  ROY OSHEROVE
ИмяМетода_Условия_Результат
[TestMethod]
public void IsValidLogFileName_ValidFile_ReturnsTrue()
{
    // arrange
    var analyzer = new LogAnalyzer();

    // act
    var result = analyzer.IsValidLogFileName("whatever.slf");

    // assert
    Assert.IsTrue(result, "filename should be valid!");
}



                     LogAnalyzer

     + IsValidLogFileName(name : string) : bool
Критерий
    хорошо оформленного теста:
•   Содержательное название
•   Короткое тело (max = 20-30 строк)
•   По шаблону AAA или GIVEN-WHEN-THEN
•   Без циклов
•   Без ветвлений (if-ов и case-ов)

• Должен легко читаться
  (literate programming)
Оповещение о том, что
     прошел такт конвейера
      должно увеличивать
значение времени жизни на один


          дано: новая деталь,
когда: оповещаем ее о такте конвейера,
   тогда: время жизни становится 1
@Test
public void shouldIncrementLifeTimeDuringTick() {
    // given
    final Item item = new Item();
    // when
    item.tick();
    // then
    assertThat(item.lifeTime()).isEqualTo(1);
}
[TestMethod]
public void ShouldIncrementLifeTimeDuringTick()
{
    // given
    var item = new Item();
    // when
    item.Tick();
    // then
    item.LifeTime.Should().Be(1);
}
Это примитивный пример
   теста на состояние
public class Item {
    public int lifeTime() { return lifeTime; }
    public void tick() { lifeTime++; }
    private int lifeTime;
}



public class Item
{
    public int LifeTime { get; private set; }
    public void Tick() { LifeTime++; }
}
Conveyor

tick(Item[*]):Item[*]

             workers
         *
       Worker                         Item




                       queue
  enqueue(Item[*])             * lifeTime():int
  tick():Item[*]                tick()
Worker




queue
Рабочий ничего не обрабатывает,
       если нет деталей

              x

  У вновь созданного рабочего
 входная очередь деталей пуста
public class WorkerTest {
    @Test
    public void shouldReturnNothingIfNothingToDo() {
        // given
       final Worker worker = new Worker();
       // when
       final List<Item> output = worker.tick();
       // then
       assertThat(output).isEmpty();
}
[TestClass]
public class WorkerTest
{
    [TestMethod]
    public void ShouldReturnNothingIfNothingToDo()
    {
        // given
        var worker = new Worker();
        // when
        var output = worker.Tick();
        // then
        output.Should().BeEmpty();
    }
Если во время обработки
 на кубике выпало значение
большее количества деталей
          в очереди,
  то рабочий обрабатывает
    все детали в очереди
      (и больше ничего)
Conveyor

tick(Item[*]):Item[*]

             workers
         *
       Worker                         Item




                       queue
  enqueue(Item[*])             * lifeTime():int
  tick():Item[*]                tick()
             dice
         1
        Dice

    roll():int
Dice
               Worker




queue
Dependency Injection (DI)
  через конструктор



         Worker
    Worker(Dice)
    enqueue(Item[*])
    tick():Item[*]
@Test
public void shouldProcessNotGreaterThanItemsInQueue() {
    // given
    final Dice dice = createDiceStub(4);
    final Worker worker = new Worker(dice);

    final List<Item> items = Arrays.asList(
                             new Item(), new Item());
    worker.enqueue(items);

    // when
    final List<Item> output = worker.tick();

    // then
    assertThat(output).isEqualTo(items);
}
[TestMethod]
public void ShouldProcessNotGreaterThanItemsInQueue()
{
    // given
    var dice = CreateDiceStub(4);
    var worker = new Worker(dice);

    var items = new[] { new Item(), new Item() };
    worker.Enqueue(items);

    // when
    var output = worker.Tick();

    // then
    output.Should().Equal(items);
}
4



enqueue   tick      Dice

                 roll():int




                  DiceStub

                 roll():int
import static org.mockito.Mockito.*;
…

private Dice createDiceStub(int rollValue) {
    final Dice dice = mock(Dice.class);
    when(dice.roll()).thenReturn(rollValue);
    return dice;
}



using Moq;
…

private Dice CreateDiceStub(int rollValue) {
    var diceMock = new Mock<Dice>();
    diceMock.Setup(d => d.Roll()).Returns(rollValue);
    return diceMock.Object;
}
Хотя мы и воспользовались
          mock-объектом,
это всё равно, по большому счету,
         тест на состояние
Если во время обработки
на кубике выпало значение N,
 меньше количества деталей
         в очереди,
  то обрабатывается только
первые N деталей из очереди
@Test
public void shouldProcessNotGreaterThanRolledValue() {
    // given
    final int rollValue = 3;
    final Dice dice = createDiceStub(rollValue);
    final Worker worker = new Worker(dice);

    final List<Item> items = Arrays.asList(
        new Item(), new Item(), new Item(), new Item());
    worker.enqueue(items);

    // when
    final List<Item> output = worker.tick();

    // then
    assertThat(output).isEqualTo(
        items.subList(0, rollValue));
}
Еще аналогичные тесты:
• Проверяем, что enqueue() добавляет в
  очередь

• Проверяем, что tick() удаляет из очереди
  обработанные детали
Тупой, но важный тест:

     Во время Worker.tick()
        кубик бросается
        ровно один раз!
Во время тренингов
    доводилось встречать такой код:

public List<Item> tick() {
    final List<Item> output = new LinkedList<Item>();

    for (int i = 0; i < dice.roll(); i++) {

        if (queue.isEmpty()) break;
        output.add(queue.poll());
    }

    return output;
}
Всё работает,
       но распределение…
P(1) = 1/6 = 0.1(6)
P(2) = 5/18 = 0.2(7)
P(3) = 5/18 = 0.2(7)
P(4) = 5/27 = 0.(185)
P(5) = 25/324  0.077
P(6) = 5/324  0.0154
@Test
public void shouldRollDiceOnlyOnceDuringTick() {
    // given
    final Dice dice = createDiceStub(3);
    final Worker worker = new Worker(dice);

    final List<Item> items = Arrays.asList(
        new Item(), new Item(),
        new Item(), new Item());
    worker.enqueue(items);

    // when
    worker.tick();

    // then
    verify(dice, times(1)).roll();
}
[TestMethod]
public void ShouldRollDiceOnlyOnceDuringTick()
{
    // given
    var diceMock = new Mock<Dice>();
    diceMock.Setup(d => d.Roll()).Returns(3);
    var dice = diceMock.Object;

    var worker = new Worker(dice);

    var items = new[] { new Item(), new Item(),
                        new Item(), new Item() };
    worker.Enqueue(items);

    // when
    worker.Tick();

    // then
    diceMock.Verify(d => d.Roll(), Times.Once());
}
Это примитивный пример
      теста на поведение:
мы проверили как взаимодействует
  наш объект с другим объектом
На состояние          На поведение


               mock




    Объект              Объект
Закрепим материал:
• Проверим, что во время Worker.tick()
  вызывается Item.tick() для всех деталей,
  находящихся в очереди на начало
  tick()-а

• Это можно проверить, не прибегая к
  mock-ам – через значение lifeTime(), но
  тогда мы тестируем два класса сразу, а
  не один в изоляции
@Test
public void shouldCallTickForAllItemsInQueue() {
    // given
    final Dice dice = createDiceStub(1);
    final Worker worker = new Worker(dice);

    final Item firstItem = mock(Item.class);
    final Item secondItem = mock(Item.class);

    final List<Item> items = Arrays.asList(
        firstItem, secondItem);
    worker.enqueue(items);

    // when
    worker.tick();

    // then
    verify(firstItem, times(1)).tick();
    verify(secondItem, times(1)).tick();
}
[TestMethod]
public void ShouldCallTickForAllItemsInQueue()
{
    // given
    var dice = CreateDiceStub(1);
    var worker = new Worker(dice);

    var firstItemMock = new Mock<Item>();
    var secondItemMock = new Mock<Item>();

    worker.Enqueue(new[] { firstItemMock.Object,
                           secondItemMock.Object });

    // when
    worker.Tick();

    // then
    firstItemMock.Verify(i => i.Tick(), Times.Once());
    secondItemMock.Verify(i => i.Tick(), Times.Once());
}
Совет «по случаю»
•   Избегайте имен переменных item1, item2 и т.п.
•   Точно запутаетесь и опечатаетесь
•   Лучше говорящие имена
•   Или на худой конец: firstItem, secondItem и т.п.
Переходим к самому
   интересному
Conveyor

tick(Item[*]):Item[*]

             workers
         *
       Worker                         Item




                       queue
  enqueue(Item[*])             * lifeTime():int
  tick():Item[*]                tick()
             dice
         1
        Dice

    roll():int
Как тестировать?
Тесты на состояние:
• Можно придумать несколько тестовых
  сценариев (разрисовать на бумажке)
  o в стиле «Контрольный пример» из начала презентации


• Проблемы:
  o Но как они помогут написать реализацию?
  o Какие тесты написать первыми, а какие потом?
  o Как быть уверенным, что протестированы все случаи и
    нюансы? (полнота покрытия)
  o Как эти тесты будут соотноситься со спецификацией?
    (test == executable specification)
Напомним спецификацию:
1. То, что подается на вход конвейера, сражу же
   оказывается в очереди первого рабочего, т.е. до
   начала обработки им деталей

2. То, что обработал последний рабочий, является
   выходом конвейера за соответствующий цикл

3. Для всех остальных рабочих их результат работы
   попадает в очередь к следующему рабочему,
   но уже после того, как тот произвел обработку
Min  Max
                    Кол-
             День          Cycle Cycle
                     во
                           Time Time
              1      0      -      -
        N     2      0      -      -
              3      2      3      3
              4      3      3      4
              5      1      4      4




Cycle Time
Тесты на поведение
           позволяют
протестировать эту спецификацию
           один в один
1. То, что подается на вход конвейера, сражу же
   оказывается в очереди первого рабочего, т.е. до
   начала обработки им деталей

2. То, что обработал последний рабочий, является
   выходом конвейера за соответствующий цикл

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




1. enqueue( )
2. tick




                       conveyor             Conveyor
   tick( )

                                      Conveyor(Worker[*])
                                      tick(Item[*]):Item[*]
@Test
public void shouldEnqueueInputToFirstWorkerBeforeProcessing() {
    // given
    final List<Item> someInput = Arrays.asList(
             new Item(), new Item(), new Item());

    final Worker firstWorker = mock(Worker.class);
    final Worker secondWorker = mock(Worker.class);
    final List<Worker> workers = Arrays.asList(
            firstWorker, secondWorker);

    final Conveyor conveyor = new Conveyor(workers);

    // when
    conveyor.tick(someInput);

    // then
    final InOrder order = inOrder(firstWorker);
    order.verify(firstWorker, times(1)).enqueue(someInput);
    order.verify(firstWorker, times(1)).tick();
}
[TestMethod]
public void ShouldEnqueueInputToFirstWorkerBeforeProcessing() {
    // given
    var someInput = new[] { new Item(), new Item };

    var callSequence = MoqSequence.Create();
    var firstWorkerMock = new Mock<Worker>();
    firstWorkerMock.Setup(w => w.Enqueue(someInput))
        .InSequence(callSequence);
    firstWorkerMock.Setup(w => w.Tick())
        .InSequence(callSequence);
    var firstWorker = firstWorkerMock.Object;

    var secondWorker = new Mock<Worker>().Object;

    var conveyor = new Conveyor(new[]{firstWorker, secondWorker });

    // when
    conveyor.Tick(someInput);

    // then
    callSequence.Verify();
}
Недостаток Moq:
нет «встроенной» поддержки
последовательностей вызовов




                  Moq.Sequences.dll
public static class MoqSequence {
    public interface ISequence { void Verify(); }
    public static ISequence Create() { return new Sequence(); }
    public static void InSequence(this ICallback mock,
                                  ISequence sequence) {
        var seq = (Sequence)sequence;
        var id = seq.GetNextCallId();
        mock.Callback(() => seq.LogCall(id));
    }

    private class Sequence : ISequence {
        public int GetNextCallId() { return nextCallId++; }
        public void LogCall(int id) { calls.Add(id); }
        public void Verify() {
            calls.Count.Should().Be(nextCallId,
                "it's expected count of calls in sequence");
            for (var i = 0; i < calls.Count; i++) {
                calls[i].Should().Be(i,
                    "wrong call sequence in position {0} ", i);
            }
        }
        private int nextCallId;
        private readonly IList<int> calls = new List<int>();
    }
}
1. То, что подается на вход конвейера, сражу же
   оказывается в очереди первого рабочего, т.е. до
   начала обработки им деталей

2. То, что обработал последний рабочий, является
   выходом конвейера за соответствующий цикл

3. Для всех остальных рабочих их результат работы
   попадает в очередь к следующему рабочему,
   но уже после того, как тот произвел обработку
@Test
public void shouldReturnOutputOfLastWorker() {
    // given
    final List<Item> someOutput = Arrays.asList(
             new Item(), new Item());
    final Worker firstWorker = mock(Worker.class);
    final Worker secondWorker = mock(Worker.class);
    when(secondWorker.tick()).thenReturn(someOutput);

    final List<Worker> workers = Arrays.asList(
            firstWorker, secondWorker);
    final Conveyor conveyor = new Conveyor(workers);

    final List<Item> someInput = Arrays.asList(new Item());

    // when
    final List<Item> output = conveyor.tick(someInput);

    // then
    assertThat(output).isEqualTo(someOutput);
}
Это можно было проверить,
     создав реальных Worker-ов,
«накормив» заранее второго нужными
              Item-ами,
 но такие тесты уже больше похожи
         на интеграционные
Mock-и очень удобны,
  чтобы имитировать любое
    необходимое состояние
      стороннего объекта,
   не связываясь с длинной
цепочкой вызовов, необходимой
  для приведения реального
    объекта в это состояние
Fake   Mock   Stub, Dummy
1. То, что подается на вход конвейера, сражу же
   оказывается в очереди первого рабочего, т.е. до
   начала обработки им деталей

2. То, что обработал последний рабочий, является
   выходом конвейера за соответствующий цикл

3. Для всех остальных рабочих их результат работы
   попадает в очередь к следующему рабочему,
   но уже после того, как тот произвел обработку
conveyor                firstWorker   secondWorker   thirdWorker


tick()
                 enqueue()

                  tick()

             
                  tick()

             
                 enqueue()

                  tick()

             
                 enqueue()

  
@Test public void
shouldEnqueueOutputOfPreviousWorkerToTheNextAfterProcessing() {
    // given
    final List<Item> outputOfFirstWorker = Arrays.asList(
             new Item(), new Item());
    final List<Item> outputOfSecondWorker = Arrays.asList(
             new Item(), new Item(), new Item());

    final Worker firstWorker = mock(Worker.class);
    when(firstWorker.tick()).thenReturn(outputOfFirstWorker);

    final Worker secondWorker = mock(Worker.class);
    when(secondWorker.tick()).thenReturn(outputOfSecondWorker);

    final Worker thirdWorker = mock(Worker.class);

    final List<Worker> workers = Arrays.asList(
            firstWorker, secondWorker, thirdWorker);

    final Conveyor conveyor = new Conveyor(workers);

    final List<Item> someInput = Arrays.asList(new Item());
// when
    conveyor.tick(someInput);

    // then
    InOrder secondWorkerOrder = inOrder(secondWorker);
    secondWorkerOrder.verify(secondWorker).tick();
    secondWorkerOrder.verify(secondWorker)
        .enqueue(outputOfFirstWorker);

    InOrder thirdWorkerOrder = inOrder(thirdWorker);
    thirdWorkerOrder.verify(thirdWorker).tick();
    thirdWorkerOrder.verify(thirdWorker)
        .enqueue(outputOfSecondWorker);
}
Тест на поведение – это проверка,
что код соответствует задуманной
 диаграмме последовательности
Реализация
public List<Item> tick(final List<Item> input) {
    if (workers.isEmpty()) { return input; }

    final Worker firstWorker = workers.get(0);
    firstWorker.enqueue(input);
    List<Item> output = firstWorker.tick();

    for (int i = 1; i < workers.size(); i++) {
        final Worker worker = workers.get(i);
        final List<Item> tmp = worker.tick();
        worker.enqueue(output);
        output = tmp;
    }

    return output;
}
public virtual IList<Item> Tick(IList<Item> input)
{
    if (workers.Count == 0) return input;

    var firstWorker = workers.First();
    firstWorker.Enqueue(input);
    var lastOutput = firstWorker.Tick();

    foreach (var worker in workers.Skip(1))
    {
        var tmp = worker.Tick();
        worker.Enqueue(lastOutput);
        lastOutput = tmp;
    }

    return lastOutput;
}
Полный код
ZIP-архив


http://tinyurl.com/tdd-demo-for-xpdays

                10 Mb
• Java
  o junit + mockito + fest
  o проект NetBeans 7.0

• .Net (C#)
  o ms unit + moq + fluentAssertions
  o проект VS 2010

• C++
  o google test + google mock
  o проект VS 2010

• PHP
  o phpUnit
  o проект PhpStorm 3.0
WARNING:
          В коде не выделены
 интерфейсы для Item, Dice и Worker
только в целях «упрощения» примера.

      Выделение интерфейсов
предпочтительнее перекрытия самих
   классов с функциональностью.

      «interface»
         Dice         RandomDice

    roll():int
Итого
Плюсы тестов на поведение
• Просто писать (когда привыкнешь)
  o не нужно долго и мучительно приводить окружение в нужное
    состояние
• Являются истинными unit-тестами
  o проверяют функционал класса в изоляции от всех остальных
• Хорошо отражают спецификации и дают
  уверенность в хорошем покрытии кода
  o executable specification
• Принуждают к модульному дизайну
  o SRP, LSP, DIP, ISP
• Позволяют разрабатывать функционал
  сверху-вниз от сценариев использования
  o а не снизу вверх от данных
1        Conveyor

tick(Item[*]):Item[*]

                 workers
             *
    2       Worker
                                     3    Item




                           queue
    enqueue(Item[*])               * lifeTime():int
    tick():Item[*]                  tick()
                 dice
             1
        4   Dice

        roll():int
Минусы тестов на поведение
• Чтобы ими овладеть, требуется ментальный
  сдвиг
• Проверяют, что код работает так, как вы
  ожидаете, но это не значит, что он работает
  правильно
  o этот недостаток легко снимается небольшим количеством
    интеграционных тестов
• Не весь функционал можно так
  протестировать
• Тесты хрупкие
  o изменение в реализации ломает тесты
• Требуют выделения интерфейсов или
  виртуальности методов
  o Обычно не проблема. А если проблема, то используйте «быстрые
    mock-и» на C++ templates
Mock Hell
• Чтобы его избежать,
  очень важно соблюдать ISP
  o Широко используемыми могут быть только
    стабильные интерфейсы
Mockist vs. Classicist



Mockist + Classicist
Спасибо за внимание!

        Вопросы?

              @bibigine
        bibigine@gmail.com
      http://tinyurl.com/bibigine
  http://www.slideshare.net/bibigine

More Related Content

What's hot

10. java lecture generics&collections
10. java lecture generics&collections10. java lecture generics&collections
10. java lecture generics&collectionsMERA_school
 
RDSDataSource: Promises
RDSDataSource: PromisesRDSDataSource: Promises
RDSDataSource: PromisesRAMBLER&Co
 
Тестирование spring boot приложений
Тестирование spring boot приложенийТестирование spring boot приложений
Тестирование spring boot приложенийSemyonKirekov
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey TeplyakovAlex Tumanoff
 
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAlex Tumanoff
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture libraryMERA_school
 
Java осень 2013 лекция 5-1
Java осень 2013 лекция 5-1Java осень 2013 лекция 5-1
Java осень 2013 лекция 5-1Technopark
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRAMBLER&Co
 
Влад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseВлад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseCocoaHeads
 
Поговорим о JavaScript, основы и современные тенденции развития языка
Поговорим о JavaScript, основы и современные тенденции развития языкаПоговорим о JavaScript, основы и современные тенденции развития языка
Поговорим о JavaScript, основы и современные тенденции развития языкаAlexander Kucherenko
 
3. java lecture classes
3. java lecture classes3. java lecture classes
3. java lecture classesMERA_school
 
Сенцов Сергей "Приемы оптимизаций Desktop приложений"
Сенцов Сергей "Приемы оптимизаций Desktop приложений"Сенцов Сергей "Приемы оптимизаций Desktop приложений"
Сенцов Сергей "Приемы оптимизаций Desktop приложений"Yulia Tsisyk
 
Java осень 2012 лекция 7
Java осень 2012 лекция 7Java осень 2012 лекция 7
Java осень 2012 лекция 7Technopark
 
Multiprocessor Programming Intro (lecture 3)
Multiprocessor Programming Intro (lecture 3)Multiprocessor Programming Intro (lecture 3)
Multiprocessor Programming Intro (lecture 3)Dmitry Tsitelov
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)Evgeny Kaziak
 
Объекты в ECMAScript | Odessa Frontend Meetup #16
Объекты в ECMAScript | Odessa Frontend Meetup #16Объекты в ECMAScript | Odessa Frontend Meetup #16
Объекты в ECMAScript | Odessa Frontend Meetup #16OdessaFrontend
 
Проектирование программных систем. Занятие 10
Проектирование программных систем. Занятие 10Проектирование программных систем. Занятие 10
Проектирование программных систем. Занятие 10Dima Dzuba
 

What's hot (20)

10. java lecture generics&collections
10. java lecture generics&collections10. java lecture generics&collections
10. java lecture generics&collections
 
RDSDataSource: Promises
RDSDataSource: PromisesRDSDataSource: Promises
RDSDataSource: Promises
 
Тестирование spring boot приложений
Тестирование spring boot приложенийТестирование spring boot приложений
Тестирование spring boot приложений
 
JRebel
JRebelJRebel
JRebel
 
Deep Dive C# by Sergey Teplyakov
Deep Dive  C# by Sergey TeplyakovDeep Dive  C# by Sergey Teplyakov
Deep Dive C# by Sergey Teplyakov
 
Async clinic by by Sergey Teplyakov
Async clinic by by Sergey TeplyakovAsync clinic by by Sergey Teplyakov
Async clinic by by Sergey Teplyakov
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture library
 
Java осень 2013 лекция 5-1
Java осень 2013 лекция 5-1Java осень 2013 лекция 5-1
Java осень 2013 лекция 5-1
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
 
Влад Ковташ — Yap Database
Влад Ковташ — Yap DatabaseВлад Ковташ — Yap Database
Влад Ковташ — Yap Database
 
Поговорим о JavaScript, основы и современные тенденции развития языка
Поговорим о JavaScript, основы и современные тенденции развития языкаПоговорим о JavaScript, основы и современные тенденции развития языка
Поговорим о JavaScript, основы и современные тенденции развития языка
 
Bytecode
BytecodeBytecode
Bytecode
 
Gradle
GradleGradle
Gradle
 
3. java lecture classes
3. java lecture classes3. java lecture classes
3. java lecture classes
 
Сенцов Сергей "Приемы оптимизаций Desktop приложений"
Сенцов Сергей "Приемы оптимизаций Desktop приложений"Сенцов Сергей "Приемы оптимизаций Desktop приложений"
Сенцов Сергей "Приемы оптимизаций Desktop приложений"
 
Java осень 2012 лекция 7
Java осень 2012 лекция 7Java осень 2012 лекция 7
Java осень 2012 лекция 7
 
Multiprocessor Programming Intro (lecture 3)
Multiprocessor Programming Intro (lecture 3)Multiprocessor Programming Intro (lecture 3)
Multiprocessor Programming Intro (lecture 3)
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)
 
Объекты в ECMAScript | Odessa Frontend Meetup #16
Объекты в ECMAScript | Odessa Frontend Meetup #16Объекты в ECMAScript | Odessa Frontend Meetup #16
Объекты в ECMAScript | Odessa Frontend Meetup #16
 
Проектирование программных систем. Занятие 10
Проектирование программных систем. Занятие 10Проектирование программных систем. Занятие 10
Проектирование программных систем. Занятие 10
 

Similar to Mockist vs Classicist

Rambler.iOS #3: Test-Driven Development в iOS
Rambler.iOS #3: Test-Driven Development в iOSRambler.iOS #3: Test-Driven Development в iOS
Rambler.iOS #3: Test-Driven Development в iOSRAMBLER&Co
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийAndrey Akinshin
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийMikhail Shcherbakov
 
2-е занятие курса iPhone разработки в ГУ-ВШЭ
2-е занятие курса iPhone разработки в ГУ-ВШЭ2-е занятие курса iPhone разработки в ГУ-ВШЭ
2-е занятие курса iPhone разработки в ГУ-ВШЭOleg Parinov
 
Продолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийПродолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийAndrey Akinshin
 
Разработка крупного Standalone проекта на юнити: улучшаем производительность
Разработка крупного Standalone проекта на юнити: улучшаем производительностьРазработка крупного Standalone проекта на юнити: улучшаем производительность
Разработка крупного Standalone проекта на юнити: улучшаем производительностьВадим Воробьев
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кодаAndrey Karpov
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода Pavel Tsukanov
 
TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.PyNSK
 
Reliable soft-1
Reliable soft-1Reliable soft-1
Reliable soft-1mnavern
 
Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"LogeekNightUkraine
 
Опыт применения активных объектов во встраиваемых системах. Архитектурные асп...
Опыт применения активных объектов во встраиваемых системах. Архитектурные асп...Опыт применения активных объектов во встраиваемых системах. Архитектурные асп...
Опыт применения активных объектов во встраиваемых системах. Архитектурные асп...Sigma Software
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerAnton Arhipov
 
Memory managment in i os (1)
Memory managment in i os (1)Memory managment in i os (1)
Memory managment in i os (1)it-park
 
Memory managment in i os
Memory managment in i osMemory managment in i os
Memory managment in i osit-park
 
Victor Kuliamin.CSEDays
Victor Kuliamin.CSEDaysVictor Kuliamin.CSEDays
Victor Kuliamin.CSEDaysLiloSEA
 
Presentation kp-1
Presentation kp-1Presentation kp-1
Presentation kp-1student_kai
 

Similar to Mockist vs Classicist (20)

Rambler.iOS #3: Test-Driven Development в iOS
Rambler.iOS #3: Test-Driven Development в iOSRambler.iOS #3: Test-Driven Development в iOS
Rambler.iOS #3: Test-Driven Development в iOS
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложений
 
Распространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложенийРаспространённые ошибки оценки производительности .NET-приложений
Распространённые ошибки оценки производительности .NET-приложений
 
2-е занятие курса iPhone разработки в ГУ-ВШЭ
2-е занятие курса iPhone разработки в ГУ-ВШЭ2-е занятие курса iPhone разработки в ГУ-ВШЭ
2-е занятие курса iPhone разработки в ГУ-ВШЭ
 
Продолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложенийПродолжаем говорить о микрооптимизациях .NET-приложений
Продолжаем говорить о микрооптимизациях .NET-приложений
 
Разработка крупного Standalone проекта на юнити: улучшаем производительность
Разработка крупного Standalone проекта на юнити: улучшаем производительностьРазработка крупного Standalone проекта на юнити: улучшаем производительность
Разработка крупного Standalone проекта на юнити: улучшаем производительность
 
статический анализ кода
статический анализ кодастатический анализ кода
статический анализ кода
 
Статический анализ кода
Статический анализ кода Статический анализ кода
Статический анализ кода
 
TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.
 
Reliable soft-1
Reliable soft-1Reliable soft-1
Reliable soft-1
 
Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"Sergii Tsypanov "Performance 1001 Tips"
Sergii Tsypanov "Performance 1001 Tips"
 
Опыт применения активных объектов во встраиваемых системах. Архитектурные асп...
Опыт применения активных объектов во встраиваемых системах. Архитектурные асп...Опыт применения активных объектов во встраиваемых системах. Архитектурные асп...
Опыт применения активных объектов во встраиваемых системах. Архитектурные асп...
 
C sharp deep dive
C sharp deep diveC sharp deep dive
C sharp deep dive
 
C# Deep Dive
C# Deep DiveC# Deep Dive
C# Deep Dive
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
 
Memory managment in i os (1)
Memory managment in i os (1)Memory managment in i os (1)
Memory managment in i os (1)
 
Memory managment in i os
Memory managment in i osMemory managment in i os
Memory managment in i os
 
Victor Kuliamin.CSEDays
Victor Kuliamin.CSEDaysVictor Kuliamin.CSEDays
Victor Kuliamin.CSEDays
 
Squeek School #7
Squeek School #7Squeek School #7
Squeek School #7
 
Presentation kp-1
Presentation kp-1Presentation kp-1
Presentation kp-1
 

More from Andrey Bibichev

О usability водопроводных кранов
О usability водопроводных крановО usability водопроводных кранов
О usability водопроводных крановAndrey Bibichev
 
Geeks vs Managers (part 2)
Geeks vs Managers (part 2)Geeks vs Managers (part 2)
Geeks vs Managers (part 2)Andrey Bibichev
 
Фрактальная природа IT-проектов (блиц)
Фрактальная природа IT-проектов (блиц)Фрактальная природа IT-проектов (блиц)
Фрактальная природа IT-проектов (блиц)Andrey Bibichev
 
Usability-for-programmers
Usability-for-programmersUsability-for-programmers
Usability-for-programmersAndrey Bibichev
 
Natural User Interface (WUDRU-2011)
Natural User Interface (WUDRU-2011)Natural User Interface (WUDRU-2011)
Natural User Interface (WUDRU-2011)Andrey Bibichev
 
Архитектура в Agile: слабая связность
Архитектура в Agile: слабая связностьАрхитектура в Agile: слабая связность
Архитектура в Agile: слабая связностьAndrey Bibichev
 
Пользовательский автоматизм
Пользовательский автоматизмПользовательский автоматизм
Пользовательский автоматизмAndrey Bibichev
 
Обзор Feature-Driven Development и Domain-Driven Design
Обзор Feature-Driven Development и Domain-Driven DesignОбзор Feature-Driven Development и Domain-Driven Design
Обзор Feature-Driven Development и Domain-Driven DesignAndrey Bibichev
 
О текстовом вводе замолвите слово
О текстовом вводе замолвите словоО текстовом вводе замолвите слово
О текстовом вводе замолвите словоAndrey Bibichev
 
Проектирование больших ИС в Agile (статья)
Проектирование больших ИС в Agile (статья)Проектирование больших ИС в Agile (статья)
Проектирование больших ИС в Agile (статья)Andrey Bibichev
 
Проектирование больших ИС в Agile
Проектирование больших ИС в AgileПроектирование больших ИС в Agile
Проектирование больших ИС в AgileAndrey Bibichev
 
Enterprise Level Agile The Art Of Start
Enterprise Level Agile   The Art Of StartEnterprise Level Agile   The Art Of Start
Enterprise Level Agile The Art Of StartAndrey Bibichev
 
Humane Interface (Гуманный интерфейс)
Humane Interface (Гуманный интерфейс)Humane Interface (Гуманный интерфейс)
Humane Interface (Гуманный интерфейс)Andrey Bibichev
 

More from Andrey Bibichev (20)

О usability водопроводных кранов
О usability водопроводных крановО usability водопроводных кранов
О usability водопроводных кранов
 
Geeks vs Managers (part 2)
Geeks vs Managers (part 2)Geeks vs Managers (part 2)
Geeks vs Managers (part 2)
 
Фрактальная природа IT-проектов (блиц)
Фрактальная природа IT-проектов (блиц)Фрактальная природа IT-проектов (блиц)
Фрактальная природа IT-проектов (блиц)
 
Usability-for-programmers
Usability-for-programmersUsability-for-programmers
Usability-for-programmers
 
Geeks vs Managers
Geeks vs ManagersGeeks vs Managers
Geeks vs Managers
 
Tdd and decomposition
Tdd and decompositionTdd and decomposition
Tdd and decomposition
 
Natural User Interface (WUDRU-2011)
Natural User Interface (WUDRU-2011)Natural User Interface (WUDRU-2011)
Natural User Interface (WUDRU-2011)
 
Puasson burning
Puasson burningPuasson burning
Puasson burning
 
Архитектура в Agile: слабая связность
Архитектура в Agile: слабая связностьАрхитектура в Agile: слабая связность
Архитектура в Agile: слабая связность
 
Пользовательский автоматизм
Пользовательский автоматизмПользовательский автоматизм
Пользовательский автоматизм
 
Augmented Reality
Augmented RealityAugmented Reality
Augmented Reality
 
Agile: Think different
Agile: Think differentAgile: Think different
Agile: Think different
 
BDD
BDDBDD
BDD
 
DDD Workshop
DDD WorkshopDDD Workshop
DDD Workshop
 
Обзор Feature-Driven Development и Domain-Driven Design
Обзор Feature-Driven Development и Domain-Driven DesignОбзор Feature-Driven Development и Domain-Driven Design
Обзор Feature-Driven Development и Domain-Driven Design
 
О текстовом вводе замолвите слово
О текстовом вводе замолвите словоО текстовом вводе замолвите слово
О текстовом вводе замолвите слово
 
Проектирование больших ИС в Agile (статья)
Проектирование больших ИС в Agile (статья)Проектирование больших ИС в Agile (статья)
Проектирование больших ИС в Agile (статья)
 
Проектирование больших ИС в Agile
Проектирование больших ИС в AgileПроектирование больших ИС в Agile
Проектирование больших ИС в Agile
 
Enterprise Level Agile The Art Of Start
Enterprise Level Agile   The Art Of StartEnterprise Level Agile   The Art Of Start
Enterprise Level Agile The Art Of Start
 
Humane Interface (Гуманный интерфейс)
Humane Interface (Гуманный интерфейс)Humane Interface (Гуманный интерфейс)
Humane Interface (Гуманный интерфейс)
 

Recently uploaded (9)

2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf2023 Q4. The Ransomware report. [RU].pdf
2023 Q4. The Ransomware report. [RU].pdf
 
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdfMalware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
Malware. DCRAT (DARK CRYSTAL RAT) [RU].pdf
 
CVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdfCVE. The Fortra's GoAnywhere MFT [RU].pdf
CVE. The Fortra's GoAnywhere MFT [RU].pdf
 
Ransomware_Q3 2023. The report [RU].pdf
Ransomware_Q3 2023.  The report [RU].pdfRansomware_Q3 2023.  The report [RU].pdf
Ransomware_Q3 2023. The report [RU].pdf
 
MS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdfMS Navigating Incident Response [RU].pdf
MS Navigating Incident Response [RU].pdf
 
Cyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdfCyberprint. Dark Pink Apt Group [RU].pdf
Cyberprint. Dark Pink Apt Group [RU].pdf
 
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
Cyber Defense Doctrine Managing the Risk Full Applied Guide to Organizational...
 
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdfСИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
СИСТЕМА ОЦЕНКИ УЯЗВИМОСТЕЙ CVSS 4.0 / CVSS v4.0 [RU].pdf
 
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
ИСТОЧНИКИ ИННОВАЦИОННОСТИ КИТАЯ (ПО ВЕРСИИ DGAP) | The Sources of China’s Inn...
 

Mockist vs Classicist

  • 1. Тесты на поведение супротив тестов на состояние Бибичев Андрей декабрь 2011
  • 2. @bibigine Андрей Бибичев • E-mail: bibigine@gmail.com • Twitter: @bibigine • Profile: http://tinyurl.com/bibigine • Slideshare: http://www.slideshare.net/bibigine
  • 3. Для кого и зачем? • Beginner o хорошая точка входа • Intermediate o поможет лучше всё структурировать в голове и объяснять коллегам • Advanced o можно использовать для обучения и проверки других
  • 4. Min Max Кол- День Cycle Cycle во Time Time 1 0 - - N 2 0 - - 3 2 3 3 4 3 3 4 5 1 4 4 Cycle Time
  • 5. Контрольный пример: Min Max Кол- День Cycle Cycle во Time Time 1 0 - - 2 0 - - 5 3 0 - - 4 0 - - 5 1 5 5
  • 6. Conveyor tick(Item[*]):Item[*] workers * Worker Item queue enqueue(Item[*]) * lifeTime():int tick():Item[*] tick()
  • 8. Conveyor tick(Item[*]):Item[*] workers * Worker Item queue enqueue(Item[*]) * lifeTime():int tick():Item[*] tick()
  • 9. У вновь созданной детали время жизни должно равняться нулю дано: новая деталь, когда: запрашиваем у нее время жизни, тогда: получаем в результате 0
  • 10. import org.junit.Test; import static org.fest.assertions.Assertions.assertThat; public class ItemTest { @Test public void shouldHaveZeroLifeTimeAterCreation() { // given final Item item = new Item(); // when int lifeTime = item.lifeTime(); // then assertThat(lifeTime).isZero(); } FEST
  • 11. using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using FluentAssertions; [TestClass] public class ItemTest { [TestMethod] public void ShouldHaveZeroLifeTimeAterCreation() { // given var item = new Item(); // when var lifeTime = item.LifeTime; // then lifeTime.Should().Be(0); } FluentAssertions
  • 13. public class Item { public int lifeTime() { return 0; } } public class Item { public int LifeTime { get; } }
  • 14.
  • 15. Дано: арбуз + гиря 1 кг = гиря 6 кг арбуз = ? Решение: x+1=6 x=6–1=5 Ответ: 5 кг
  • 16. vs. Test… Should… { { // Arrange // GIVEN … … // Action // WHEN … … // Assertion // THEN … … } }
  • 17. Март 2006 Журнал «Better Software» Статья «Introducing BDD» Dan North
  • 19. ИмяМетода_Условия_Результат [TestMethod] public void IsValidLogFileName_ValidFile_ReturnsTrue() { // arrange var analyzer = new LogAnalyzer(); // act var result = analyzer.IsValidLogFileName("whatever.slf"); // assert Assert.IsTrue(result, "filename should be valid!"); } LogAnalyzer + IsValidLogFileName(name : string) : bool
  • 20. Критерий хорошо оформленного теста: • Содержательное название • Короткое тело (max = 20-30 строк) • По шаблону AAA или GIVEN-WHEN-THEN • Без циклов • Без ветвлений (if-ов и case-ов) • Должен легко читаться (literate programming)
  • 21.
  • 22. Оповещение о том, что прошел такт конвейера должно увеличивать значение времени жизни на один дано: новая деталь, когда: оповещаем ее о такте конвейера, тогда: время жизни становится 1
  • 23. @Test public void shouldIncrementLifeTimeDuringTick() { // given final Item item = new Item(); // when item.tick(); // then assertThat(item.lifeTime()).isEqualTo(1); }
  • 24. [TestMethod] public void ShouldIncrementLifeTimeDuringTick() { // given var item = new Item(); // when item.Tick(); // then item.LifeTime.Should().Be(1); }
  • 25. Это примитивный пример теста на состояние
  • 26. public class Item { public int lifeTime() { return lifeTime; } public void tick() { lifeTime++; } private int lifeTime; } public class Item { public int LifeTime { get; private set; } public void Tick() { LifeTime++; } }
  • 27. Conveyor tick(Item[*]):Item[*] workers * Worker Item queue enqueue(Item[*]) * lifeTime():int tick():Item[*] tick()
  • 29. Рабочий ничего не обрабатывает, если нет деталей x У вновь созданного рабочего входная очередь деталей пуста
  • 30. public class WorkerTest { @Test public void shouldReturnNothingIfNothingToDo() { // given final Worker worker = new Worker(); // when final List<Item> output = worker.tick(); // then assertThat(output).isEmpty(); }
  • 31. [TestClass] public class WorkerTest { [TestMethod] public void ShouldReturnNothingIfNothingToDo() { // given var worker = new Worker(); // when var output = worker.Tick(); // then output.Should().BeEmpty(); }
  • 32. Если во время обработки на кубике выпало значение большее количества деталей в очереди, то рабочий обрабатывает все детали в очереди (и больше ничего)
  • 33. Conveyor tick(Item[*]):Item[*] workers * Worker Item queue enqueue(Item[*]) * lifeTime():int tick():Item[*] tick() dice 1 Dice roll():int
  • 34. Dice Worker queue
  • 35. Dependency Injection (DI) через конструктор Worker Worker(Dice) enqueue(Item[*]) tick():Item[*]
  • 36. @Test public void shouldProcessNotGreaterThanItemsInQueue() { // given final Dice dice = createDiceStub(4); final Worker worker = new Worker(dice); final List<Item> items = Arrays.asList( new Item(), new Item()); worker.enqueue(items); // when final List<Item> output = worker.tick(); // then assertThat(output).isEqualTo(items); }
  • 37. [TestMethod] public void ShouldProcessNotGreaterThanItemsInQueue() { // given var dice = CreateDiceStub(4); var worker = new Worker(dice); var items = new[] { new Item(), new Item() }; worker.Enqueue(items); // when var output = worker.Tick(); // then output.Should().Equal(items); }
  • 38. 4 enqueue tick Dice roll():int DiceStub roll():int
  • 39. import static org.mockito.Mockito.*; … private Dice createDiceStub(int rollValue) { final Dice dice = mock(Dice.class); when(dice.roll()).thenReturn(rollValue); return dice; } using Moq; … private Dice CreateDiceStub(int rollValue) { var diceMock = new Mock<Dice>(); diceMock.Setup(d => d.Roll()).Returns(rollValue); return diceMock.Object; }
  • 40.
  • 41. Хотя мы и воспользовались mock-объектом, это всё равно, по большому счету, тест на состояние
  • 42. Если во время обработки на кубике выпало значение N, меньше количества деталей в очереди, то обрабатывается только первые N деталей из очереди
  • 43. @Test public void shouldProcessNotGreaterThanRolledValue() { // given final int rollValue = 3; final Dice dice = createDiceStub(rollValue); final Worker worker = new Worker(dice); final List<Item> items = Arrays.asList( new Item(), new Item(), new Item(), new Item()); worker.enqueue(items); // when final List<Item> output = worker.tick(); // then assertThat(output).isEqualTo( items.subList(0, rollValue)); }
  • 44. Еще аналогичные тесты: • Проверяем, что enqueue() добавляет в очередь • Проверяем, что tick() удаляет из очереди обработанные детали
  • 45. Тупой, но важный тест: Во время Worker.tick() кубик бросается ровно один раз!
  • 46. Во время тренингов доводилось встречать такой код: public List<Item> tick() { final List<Item> output = new LinkedList<Item>(); for (int i = 0; i < dice.roll(); i++) { if (queue.isEmpty()) break; output.add(queue.poll()); } return output; }
  • 47. Всё работает, но распределение… P(1) = 1/6 = 0.1(6) P(2) = 5/18 = 0.2(7) P(3) = 5/18 = 0.2(7) P(4) = 5/27 = 0.(185) P(5) = 25/324  0.077 P(6) = 5/324  0.0154
  • 48. @Test public void shouldRollDiceOnlyOnceDuringTick() { // given final Dice dice = createDiceStub(3); final Worker worker = new Worker(dice); final List<Item> items = Arrays.asList( new Item(), new Item(), new Item(), new Item()); worker.enqueue(items); // when worker.tick(); // then verify(dice, times(1)).roll(); }
  • 49. [TestMethod] public void ShouldRollDiceOnlyOnceDuringTick() { // given var diceMock = new Mock<Dice>(); diceMock.Setup(d => d.Roll()).Returns(3); var dice = diceMock.Object; var worker = new Worker(dice); var items = new[] { new Item(), new Item(), new Item(), new Item() }; worker.Enqueue(items); // when worker.Tick(); // then diceMock.Verify(d => d.Roll(), Times.Once()); }
  • 50. Это примитивный пример теста на поведение: мы проверили как взаимодействует наш объект с другим объектом
  • 51. На состояние На поведение mock Объект Объект
  • 52. Закрепим материал: • Проверим, что во время Worker.tick() вызывается Item.tick() для всех деталей, находящихся в очереди на начало tick()-а • Это можно проверить, не прибегая к mock-ам – через значение lifeTime(), но тогда мы тестируем два класса сразу, а не один в изоляции
  • 53. @Test public void shouldCallTickForAllItemsInQueue() { // given final Dice dice = createDiceStub(1); final Worker worker = new Worker(dice); final Item firstItem = mock(Item.class); final Item secondItem = mock(Item.class); final List<Item> items = Arrays.asList( firstItem, secondItem); worker.enqueue(items); // when worker.tick(); // then verify(firstItem, times(1)).tick(); verify(secondItem, times(1)).tick(); }
  • 54. [TestMethod] public void ShouldCallTickForAllItemsInQueue() { // given var dice = CreateDiceStub(1); var worker = new Worker(dice); var firstItemMock = new Mock<Item>(); var secondItemMock = new Mock<Item>(); worker.Enqueue(new[] { firstItemMock.Object, secondItemMock.Object }); // when worker.Tick(); // then firstItemMock.Verify(i => i.Tick(), Times.Once()); secondItemMock.Verify(i => i.Tick(), Times.Once()); }
  • 55.
  • 56. Совет «по случаю» • Избегайте имен переменных item1, item2 и т.п. • Точно запутаетесь и опечатаетесь • Лучше говорящие имена • Или на худой конец: firstItem, secondItem и т.п.
  • 57.
  • 58. Переходим к самому интересному
  • 59. Conveyor tick(Item[*]):Item[*] workers * Worker Item queue enqueue(Item[*]) * lifeTime():int tick():Item[*] tick() dice 1 Dice roll():int
  • 61. Тесты на состояние: • Можно придумать несколько тестовых сценариев (разрисовать на бумажке) o в стиле «Контрольный пример» из начала презентации • Проблемы: o Но как они помогут написать реализацию? o Какие тесты написать первыми, а какие потом? o Как быть уверенным, что протестированы все случаи и нюансы? (полнота покрытия) o Как эти тесты будут соотноситься со спецификацией? (test == executable specification)
  • 62. Напомним спецификацию: 1. То, что подается на вход конвейера, сражу же оказывается в очереди первого рабочего, т.е. до начала обработки им деталей 2. То, что обработал последний рабочий, является выходом конвейера за соответствующий цикл 3. Для всех остальных рабочих их результат работы попадает в очередь к следующему рабочему, но уже после того, как тот произвел обработку
  • 63. Min Max Кол- День Cycle Cycle во Time Time 1 0 - - N 2 0 - - 3 2 3 3 4 3 3 4 5 1 4 4 Cycle Time
  • 64. Тесты на поведение позволяют протестировать эту спецификацию один в один
  • 65. 1. То, что подается на вход конвейера, сражу же оказывается в очереди первого рабочего, т.е. до начала обработки им деталей 2. То, что обработал последний рабочий, является выходом конвейера за соответствующий цикл 3. Для всех остальных рабочих их результат работы попадает в очередь к следующему рабочему, но уже после того, как тот произвел обработку
  • 66. worker1 worker2 1. enqueue( ) 2. tick conveyor Conveyor tick( ) Conveyor(Worker[*]) tick(Item[*]):Item[*]
  • 67. @Test public void shouldEnqueueInputToFirstWorkerBeforeProcessing() { // given final List<Item> someInput = Arrays.asList( new Item(), new Item(), new Item()); final Worker firstWorker = mock(Worker.class); final Worker secondWorker = mock(Worker.class); final List<Worker> workers = Arrays.asList( firstWorker, secondWorker); final Conveyor conveyor = new Conveyor(workers); // when conveyor.tick(someInput); // then final InOrder order = inOrder(firstWorker); order.verify(firstWorker, times(1)).enqueue(someInput); order.verify(firstWorker, times(1)).tick(); }
  • 68. [TestMethod] public void ShouldEnqueueInputToFirstWorkerBeforeProcessing() { // given var someInput = new[] { new Item(), new Item }; var callSequence = MoqSequence.Create(); var firstWorkerMock = new Mock<Worker>(); firstWorkerMock.Setup(w => w.Enqueue(someInput)) .InSequence(callSequence); firstWorkerMock.Setup(w => w.Tick()) .InSequence(callSequence); var firstWorker = firstWorkerMock.Object; var secondWorker = new Mock<Worker>().Object; var conveyor = new Conveyor(new[]{firstWorker, secondWorker }); // when conveyor.Tick(someInput); // then callSequence.Verify(); }
  • 69. Недостаток Moq: нет «встроенной» поддержки последовательностей вызовов Moq.Sequences.dll
  • 70. public static class MoqSequence { public interface ISequence { void Verify(); } public static ISequence Create() { return new Sequence(); } public static void InSequence(this ICallback mock, ISequence sequence) { var seq = (Sequence)sequence; var id = seq.GetNextCallId(); mock.Callback(() => seq.LogCall(id)); } private class Sequence : ISequence { public int GetNextCallId() { return nextCallId++; } public void LogCall(int id) { calls.Add(id); } public void Verify() { calls.Count.Should().Be(nextCallId, "it's expected count of calls in sequence"); for (var i = 0; i < calls.Count; i++) { calls[i].Should().Be(i, "wrong call sequence in position {0} ", i); } } private int nextCallId; private readonly IList<int> calls = new List<int>(); } }
  • 71. 1. То, что подается на вход конвейера, сражу же оказывается в очереди первого рабочего, т.е. до начала обработки им деталей 2. То, что обработал последний рабочий, является выходом конвейера за соответствующий цикл 3. Для всех остальных рабочих их результат работы попадает в очередь к следующему рабочему, но уже после того, как тот произвел обработку
  • 72. @Test public void shouldReturnOutputOfLastWorker() { // given final List<Item> someOutput = Arrays.asList( new Item(), new Item()); final Worker firstWorker = mock(Worker.class); final Worker secondWorker = mock(Worker.class); when(secondWorker.tick()).thenReturn(someOutput); final List<Worker> workers = Arrays.asList( firstWorker, secondWorker); final Conveyor conveyor = new Conveyor(workers); final List<Item> someInput = Arrays.asList(new Item()); // when final List<Item> output = conveyor.tick(someInput); // then assertThat(output).isEqualTo(someOutput); }
  • 73. Это можно было проверить, создав реальных Worker-ов, «накормив» заранее второго нужными Item-ами, но такие тесты уже больше похожи на интеграционные
  • 74. Mock-и очень удобны, чтобы имитировать любое необходимое состояние стороннего объекта, не связываясь с длинной цепочкой вызовов, необходимой для приведения реального объекта в это состояние
  • 75.
  • 76. Fake Mock Stub, Dummy
  • 77.
  • 78. 1. То, что подается на вход конвейера, сражу же оказывается в очереди первого рабочего, т.е. до начала обработки им деталей 2. То, что обработал последний рабочий, является выходом конвейера за соответствующий цикл 3. Для всех остальных рабочих их результат работы попадает в очередь к следующему рабочему, но уже после того, как тот произвел обработку
  • 79. conveyor firstWorker secondWorker thirdWorker tick() enqueue() tick()  tick()  enqueue() tick()  enqueue() 
  • 80. @Test public void shouldEnqueueOutputOfPreviousWorkerToTheNextAfterProcessing() { // given final List<Item> outputOfFirstWorker = Arrays.asList( new Item(), new Item()); final List<Item> outputOfSecondWorker = Arrays.asList( new Item(), new Item(), new Item()); final Worker firstWorker = mock(Worker.class); when(firstWorker.tick()).thenReturn(outputOfFirstWorker); final Worker secondWorker = mock(Worker.class); when(secondWorker.tick()).thenReturn(outputOfSecondWorker); final Worker thirdWorker = mock(Worker.class); final List<Worker> workers = Arrays.asList( firstWorker, secondWorker, thirdWorker); final Conveyor conveyor = new Conveyor(workers); final List<Item> someInput = Arrays.asList(new Item());
  • 81. // when conveyor.tick(someInput); // then InOrder secondWorkerOrder = inOrder(secondWorker); secondWorkerOrder.verify(secondWorker).tick(); secondWorkerOrder.verify(secondWorker) .enqueue(outputOfFirstWorker); InOrder thirdWorkerOrder = inOrder(thirdWorker); thirdWorkerOrder.verify(thirdWorker).tick(); thirdWorkerOrder.verify(thirdWorker) .enqueue(outputOfSecondWorker); }
  • 82. Тест на поведение – это проверка, что код соответствует задуманной диаграмме последовательности
  • 84. public List<Item> tick(final List<Item> input) { if (workers.isEmpty()) { return input; } final Worker firstWorker = workers.get(0); firstWorker.enqueue(input); List<Item> output = firstWorker.tick(); for (int i = 1; i < workers.size(); i++) { final Worker worker = workers.get(i); final List<Item> tmp = worker.tick(); worker.enqueue(output); output = tmp; } return output; }
  • 85. public virtual IList<Item> Tick(IList<Item> input) { if (workers.Count == 0) return input; var firstWorker = workers.First(); firstWorker.Enqueue(input); var lastOutput = firstWorker.Tick(); foreach (var worker in workers.Skip(1)) { var tmp = worker.Tick(); worker.Enqueue(lastOutput); lastOutput = tmp; } return lastOutput; }
  • 88. • Java o junit + mockito + fest o проект NetBeans 7.0 • .Net (C#) o ms unit + moq + fluentAssertions o проект VS 2010 • C++ o google test + google mock o проект VS 2010 • PHP o phpUnit o проект PhpStorm 3.0
  • 89. WARNING: В коде не выделены интерфейсы для Item, Dice и Worker только в целях «упрощения» примера. Выделение интерфейсов предпочтительнее перекрытия самих классов с функциональностью. «interface» Dice RandomDice roll():int
  • 91. Плюсы тестов на поведение • Просто писать (когда привыкнешь) o не нужно долго и мучительно приводить окружение в нужное состояние • Являются истинными unit-тестами o проверяют функционал класса в изоляции от всех остальных • Хорошо отражают спецификации и дают уверенность в хорошем покрытии кода o executable specification • Принуждают к модульному дизайну o SRP, LSP, DIP, ISP • Позволяют разрабатывать функционал сверху-вниз от сценариев использования o а не снизу вверх от данных
  • 92. 1 Conveyor tick(Item[*]):Item[*] workers * 2 Worker 3 Item queue enqueue(Item[*]) * lifeTime():int tick():Item[*] tick() dice 1 4 Dice roll():int
  • 93. Минусы тестов на поведение • Чтобы ими овладеть, требуется ментальный сдвиг • Проверяют, что код работает так, как вы ожидаете, но это не значит, что он работает правильно o этот недостаток легко снимается небольшим количеством интеграционных тестов • Не весь функционал можно так протестировать • Тесты хрупкие o изменение в реализации ломает тесты • Требуют выделения интерфейсов или виртуальности методов o Обычно не проблема. А если проблема, то используйте «быстрые mock-и» на C++ templates
  • 94. Mock Hell • Чтобы его избежать, очень важно соблюдать ISP o Широко используемыми могут быть только стабильные интерфейсы
  • 96. Спасибо за внимание! Вопросы? @bibigine bibigine@gmail.com http://tinyurl.com/bibigine http://www.slideshare.net/bibigine