У чьому різниця між Stub і Mock у РHPUnit

Stub і Mock (стаб і мок) – це дві різновидності Test Double, тобто тестового двійника. Тестовий двійник – це обʼєкт, що передається в SUT (систему, що тестується). Крім них, існує ще декілька різновидів, найпопулярніші з них: Dummy, Fake, Spy.

Test Double
            |
            -> Dummy
            |
            -> Stub
            |
            -> Mock
            |
            -> Fake
            |
            -> Spy

Найпростішим тестовим двійником є dummy, єдина функція якого – задовільнити умовам інтерфейсу. Методи цього класу не викликаються, чи їх виклик не впливає на тестуєму систему.

Для stub головне – повернути заздалегідь визначені данні, при чьому не важливо, скільки разів ці данні запитувались.

Для mock навпаки, найголовніше – це простежити що визначені методи викликались задану кількість разів. Мок може повертати дані для того, щоб тест пройшов по потрібним гілкам, проте основний акцент залишається на взаємодії тестуємої системи і мока.

Fake – це спрощена реалізація обʼєкта. Прикладом фейка може бути in-memory база даних, що збергіє данні в масив, замість того щоб робити справжній запит у базу даних.

Spy – здатний записувати взаємодію тестуємої системи з обʼєктом, які можна провалідитувати пізніше.

Тестові двійники у PHPUnit

У PHPUnit явним чином виділені сутності stub і mock. Замість dummy можна використовувати звичайний клас, стаб чи мок. Fake – зазвичай імплементується як звийчайний клас. Про використання Spy у PHPUnit є гарна стаття (див. посилання внизу).

Як використовувати stub у тесті?

// Arrange
$dependency = $this->createStub(Dependency::class);
$dependency->method('getExpression')
           ->willReturn('40 + 2');
$sut = new Calculator(dependency);

// Act
$result = $sut->process();

// Assert
$this->assertEquals(42, $result);

У прикладі вище створюється стаб, який імплементує метод getExpression, що завжди повертає строку з виразом ’40 + 2′. Далі стаб передається в обʼєкт калькулятора як залежність – це єтап arrange. На єтапі act – виконується тестуєма дія. На єтапі assert – перевіряється результат виконуємої дії.

Тестові стаби можуть повертати список завчасно вказаних значень, при послідовних викликах. Приклад з документації PHPUnit:

// Create a stub for the SomeClass class.
$stub = $this->createStub(SomeClass::class);

// Configure the stub.
$stub->method('doSomething')
            ->willReturn(1, 2, 3);

// $stub->doSomething() returns a different value each time
$this->assertSame(1, $stub->doSomething());
$this->assertSame(2, $stub->doSomething());
$this->assertSame(3, $stub->doSomething());

Також існує спрощенний синтаксис створеня мока, у якому необхідно визначити декілька функцій.

$o = $this->createConfiguredStub(
   SomeInterface::class,
   [
       'doSomething'     => 'foo',
       'doSomethingElse' => 'bar',
   ]
);

Не всі IDE підтримують синтаксис createConfiguredStub: строки з назвами методів не сприймаються як методи. Відповідно при рефакторингу коду IDE може не побачити ці методи, що призведе до падіння тесту.

Як використовувати mock у тесті?

// Arrange
$dependency = $this->createMock(Dependency::class);
$dependency->expects($this->once())
           ->method('fetch')
           ->willReturn('{"data":"..."}');
$sut = new CacheService(dependency);

// Act
$result = $sut->getData();

// Assert
$this->assertStringEndsWith('{"data":"..."}', $result);

Використання моків схоже з використанням стабів, різниця полягає у єтапі arrange, де налаштовується перевірка на виклики методів тестового двійника. У прикладі використовується обмеження once – на один виклик метода. Якщо у кінці теста метод не викликався чи викликався більше одного разу – тест впаде.

Існує 6 видів обмежень:

  • any() – будь яка кількість викликів(навіть 0)
  • never() – жодного виклику
  • atLeastOnce() – мінімум один виклик
  • once() – рівно один виклик
  • atMost(int $count) – не більше ніж count разів
  • exactly(int $count) – рівно count разів

Корисні посилання

Використання Spy у PHPUnit

Документація PHPUnit про тестові двійники

Пост Мартіна Вофлера про види тестових двійників

Book Review: “Skunk Works: A Personal Memoir of My Years at Lockheed” (quotes)

This book is about Skunk Works – a small department inside Lockheed Martin, which is known for its unique approach to work.

Book cover

The secret souse is the tight integration of the engineering team with production. The engineering team is small, but all the engineers are top-notch and work without bureaucracy and paperwork.

The most important thing for me is the style and methodology of the engineering process. Good story about the development of U2, Blackbird (my favorite aircraft, by the way), and F-117, plus some politics as a nice bonus.
It is also interesting to get to know the personalities of legendary Kelly Johnson and Ben Rich, the people who respond to the challenges and put everything into what they believe in.

A couple of quotes from the book:

Over the years we had developed the concept of using existing hardware developed and paid for by other programs to save time and money and reduce the risks of failures in prototype projects.

Despite the fact that the engineers were creating completely new, revolutionary aircraft, the ideology of Skunk Work dictated the maximum use of existing parts that have already been tested and are available.

 “riot act”—ten basic rules we worked by. A few of them: “There shall be only one object: to get a good airplane built on time.” “Engineers shall always work within a stone’s throw of the airplane being built.” “Any cause for delay shall be immediately reported to C. L. Johnson in writing by the person anticipating the delay.” “Special parts or materials shall be avoided whenever possible. Parts from stock shall be used even at the expense of added weight. Otherwise the chances of delay are too great.”

The rules was defined and everybody knew them

“I’ll teach you all you need to know about running a company in one afternoon, and we’ll both go home early to boot. You don’t need Harvard to teach you that it’s more important to listen than to talk. You can get straight A’s from all your Harvard profs, but you’ll never make the grade unless you are decisive: even a timely wrong decision is better than no decision. The final thing you’ll need to know is don’t half-heartedly wound problems—kill them dead. That’s all there is to it. Now you can run this goddam place. Now, go on home and pour yourself a drink.”

A little bit of management wisdom

We became the most successful advanced projects company in the world by hiring talented people, paying them top dollar, and motivating them into believing that they could produce a Mach 3 airplane like the Blackbird a generation or two ahead of anybody else.

I don’t want to compare the small R & D operations, but they all consist of similar close-knit, can-do, highly technical groups working on advanced and complex problems. They are all self-contained and do not require many people or big budgets,

About hiring the professionals, good salaries and management

And the last but the most important one:

“Ben, if I teach you anything, it’s this: don’t build an airplane you don’t believe in. Don’t prostitute yourself for bucks.”

Suits about any professinal field

In conclusion, I recommend this book to every manager.