метод не существует для этого фиктивного объекта - Laravel, Mockery

я пытаюсь проверить простой класс. Я следую этому руководству ( http://code.tutsplus.com/tutorials/testing-laravel-controllers--net-31456 ).

У меня есть эта ошибка при запуске тестов:

Method Mockery_0_App_Interfaces_MealTypeRepositoryInterface::getValidator() does not exist on this mock object

Я использую структуру репозитория. Итак, мой контроллер вызывает репозиторий, и он возвращает ответ Eloquent.

Я относительно новичок в php и laravel. И я начал учиться тестировать несколько дней назад, так что извините за этот беспорядочный код.

Мой тестовый пример:

class MealTypeControllerTest extends TestCase
{
public function setUp()
{
    parent::setUp();

    $this->mock = Mockery::mock('App\Interfaces\MealTypeRepositoryInterface');
    $this->app->instance('App\Interfaces\MealTypeRepositoryInterface' , $this->mock);
}
public function tearDown()
{
    Mockery::close();
}

public function testIndex()
{
    $this->mock
         ->shouldReceive('all')
         ->once()
         ->andReturn(['mealTypes' => (object)['id' => 1 , 'name' => 'jidlo']]);

    $this->call('GET' , 'mealType');

    $this->assertViewHas('mealTypes');
}

public function testStoreFails()
{
    $input = ['name' => 'x'];

    $this->mock
         ->shouldReceive('getValidator')
         ->once()
         ->andReturn(Mockery::mock(['fails' => true]));

    $this->mock
         ->shouldReceive('create')
         ->once()
         ->with($input);


    $this->call('POST' , 'mealType' , $input ); // this line throws the error

    $this->assertRedirectedToRoute('mealType.create');//->withErrors();

    $this->assertSessionHasErrors('name');
}

}

My EloquentMealTypeRepository: Ничего интересного.

class EloquentMealTypeRepository implements MealTypeRepositoryInterface
{
public function all()
{
    return MealType::all();
}

public function find($id)
{
    return MealType::find($id);
}

public function create($input)
{
    return MealType::create($input);
}

public function getValidator($input)
{
    return MealType::getValidator($input);
}
}

Моя красноречивая реализация: тоже ничего интересного.

class MealType extends Model
{
private $validator;

/**
 * The database table used by the model.
 *
 * @var string
 */
protected $table = 'meal_types';

/**
 * The attributes that are mass assignable.
 *
 * @var array
 */
protected $fillable = ['name'];

/**
 * The attributes excluded from the model's JSON form.
 *
 * @var array
 */
protected $hidden = [];

public function meals()
{
    return $this->hasMany('Meal');
}

public static function getValidator($fields)
{
    return Validator::make($fields, ['name' => 'required|min:3'] );
}
}

Мой MealTypeRepositoryInterface:

interface MealTypeRepositoryInterface
{
public function all();

public function find($id);

public function create($input);

public function getValidator($input);
}

И, наконец, Мой контроллер:

class MealTypeController extends Controller {
protected $mealType;

public function __construct(MealType $mealType)
{   
    $this->mealType = $mealType;
}


/**
 * Display a listing of the resource.
 *
 * @return Response
 */
public function index()
{
    $mealTypes = $this->mealType->all();
    return View::make('mealTypes.index')->with('mealTypes' ,$mealTypes);
}

/**
 * Show the form for creating a new resource.
 *
 * @return Response
 */
public function create()
{
    $mealType = new MealTypeEloquent;
    $action = 'MealTypeController@store';
    $method = 'POST';

    return View::make('mealTypes.create_edit', compact('mealType' , 'action' , 'method') );     
}

/**
 * Validator does not work properly in tests.
 * Store a newly created resource in storage.
 *
 * @return Response
 */
public function store(Request $request)
{
    $input = ['name' => $request->input('name')];

    $mealType = new $this->mealType;

    $v = $mealType->getValidator($input);

    if( $v->passes() )
    {
        $this->mealType->create($input);
        return Redirect::to('mealType');
    }
    else
    {
        $this->errors = $v;
        return Redirect::to('mealType/create')->withErrors($v);
    }
}

/**
 * Display the specified resource.
 *
 * @param  int  $id
 * @return Response
 */
public function show($id)
{
    return View::make('mealTypes.show' , ['mealType' => $this->mealType->find($id)]);
}

/**
 * Show the form for editing the specified resource.
 *
 * @param  int  $id
 * @return Response
 */
public function edit($id)
{
    $mealType = $this->mealType->find($id);
    $action = 'MealTypeController@update';
    $method = 'PATCH';
    return View::make('mealTypes.create_edit')->with(compact('mealType' , 'action' , 'method'));
}

/**
 * Update the specified resource in storage.
 *
 * @param  int  $id
 * @return Response
 */
public function update($id)
{
    $mealType = $this->mealType->find($id);
    $mealType->name = \Input::get('name');
    $mealType->save();
    return redirect('mealType');
}

/**
 * Remove the specified resource from storage.
 *
 * @param  int  $id
 * @return Response
 */
public function destroy($id)
{
    $this->mealType->find($id)->delete();
    return redirect('mealType');
}

}

Это должно быть все. Стоит сказать, что приложение работает, только тесты лажают. Кто-нибудь знает, почему так происходит? Я не вижу разницы между методами TestCase - testIndex и testStoreFails, почему метод "all" найден, а "getValidator" нет. Буду благодарен за любые подсказки-советы.


person Jakubasan    schedule 02.01.2015    source источник


Ответы (2)


Возможно, в стороне, но имеет непосредственное отношение к любому, кто найдет этот вопрос по его названию:

If:

  1. Вы получаете сообщение об ошибке BadMethodCallException: Method Mockery_0_MyClass::myMethod() does not exist on this mock object, и
  2. ни один из ваших макетов не улавливает ни один из методов вашего субъекта, и
  3. ваши классы загружаются автоматически (например, с помощью композитора)

затем, прежде чем создавать фиктивный объект, вам необходимо принудительно загрузить этот объект, используя эту строку кода:

spl_autoload_call('MyNamespace\MyClass'); 

Тогда вы можете издеваться над этим:

$mock = \Mockery::mock('MyNamespace\MyClass');

В моих тестах PHPUnit я часто помещаю эту первую строку в статическую функцию setUpBeforeClass(), поэтому она вызывается только один раз и изолирована от добавляемых/удаляемых тестов. Итак, класс Test выглядит так:

class MyClassTest extends PHPUnit_Framework_TestCase {
    public static function setUpBeforeClass() {
        parent::setUpBeforeClass();
        spl_autoload_call('Jodes\MyClass'); 
    }
    public function testIt(){
        $mock = \Mockery::mock('Jodes\MyClass');
    }
}

Я забыл об этом уже три раза, каждый раз тратя час или два на размышления, в чем, черт возьми, проблема!

person CL22    schedule 19.08.2015

Я нашел источник этой ошибки в контроллере. называя неправильно

$v = $mealType->getValidator($input);

вместо права

$v = $this->mealType->getValidator($input);

person Jakubasan    schedule 03.01.2015