---
title: Моки в юнит-тестах
date: 2012-03-09T10:00
tags: [tests]
---

Моки это такие классы-заглушки (и соответсвенно объекты), которые позволяют избавиться от внешних зависимостей при модульном (unit) тестировании — **ибо тестирование с зависимостями уже интеграционное или системное** и требует больших ресурсов, состояния данных и как следствие - большей сложности.

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

В PHPUnit моки встроены в PHPUnit_Framework_TestCase и легко получаются от метода **getMock**(класс, методы, ...). Сам по себе он не только даёт объект, но и определяет класс. Так что на случай использования статического метода, мок должен сработать.

```
$myMock = $this->getMock('MyParser', array('parseXLS'));
```

<!-- truncate -->

### Число вызовов

Особенность моков, в отличие от простых заглушек (stub) в изменяющемся поведении. Поведение определяется правилами которые вы составите, например:

```
$myMock->expects($this->any())
```

Это мы получаем мок которому безразлично сколько раз он будет использоваться. Но можно быть указать строже — **never**,**atLeastOnce**,**once**,**exactly**(3)

### Параметры и ожидания

Можно ограничить и параметры которые будут передаватьсяи ожидаемый результат

```
$myMock->method('parseXLS')
->with( $this->equalTo('fileAsParam.xls') )
->will($this->returnValue( 'text From XLS File' ))
```

Вместо чёткого равенства можно использовать и более гибкие greaterThan, stringContains, anything.

А вместо стандартного возвращения значения, можно ..

- **********onConsecutiveCalls********( значения через запятую ) — в зависимости от порядка вызова метода будет возвращать переданные значения**
- ********returnArgument********(индекс переданного ранее аргумента)
- ********returnCallback********(вызываемая лямбда)
- ********throwException********( Эксепшн )

### Проверка параметров

Для проверки используется ->with() с одним из встроенных методов:

```
$this->anything()
$this->contains($value)
$this->arrayHasKey($key)
$this->equalTo($value, $delta, $maxDepth)
$this->classHasAttribute($attribute)
$this->greaterThan($value)
$this->isInstanceOf($className)
$this->isType($type)
$this->matchesRegularExpression($pattern)
$this->stringContains($string, $case)
```

### Проблемы

Я сталкивался с тем что фреймворк определяет класс и как следствие если в в дальнейшем инклудите реальный код, то получаете ошибку [cannot redeclare class](http://stackoverflow.com/questions/7742390/phpunit-test-suite-cannot-redeclare-class-mocking-concrete-classes/9637162). Пока что это исправлял через использование пространства имён

Более критическая проблема с конструкторами - если вы используете parent::__construct() и при этом parent создан как мок, то можно получить `Fatal Error: Cannot call constructor in ...`

## Related

- [Kohana](/ru/blog/тестирование/unit-testirovanie-v-kohana-32/)
- [Protractor](/ru/blog/тестирование/protractor/)
- [Изолированное UI тестирование с Protractor](/ru/blog/тестирование/izolirovannoe-ui-testirovanie-s-protractor/)
