Laravel автоматизация модульного тестирования c внедрение зависимостей не работает? - PullRequest
2 голосов
/ 03 марта 2020

С Laravel Framework 5.8.36 Я пытаюсь запустить тест, который вызывает контроллер, где метод __construct использует DI, например:

class SomeController extends Controller {

public function __construct(XYZRepository $xyz_repository)
{
    $this->xyz_repository = $xyz_repository;
}

public function doThisOtherThing(Request $request, $id)
{
    try {
        return response()->json($this->xyz_repository->doTheRepoThing($id), 200);
    } catch (Exception $exception) {
        return response($exception->getMessage(), 500);
    }
}
}

Это прекрасно работает, если я запускаю код через браузер или вызовите его как вызов API в почтальоне, но когда я вызываю метод doThisOtherThing из своего теста, я получаю следующую ошибку:

ArgumentCountError: слишком мало аргументов для функции App \ Http \ Controllers \ SomeController :: __ construct (), 0 передано в / var / www/tests/Unit/Controllers/SomeControllerTest.php в строке 28 и ровно 1 ожидается

Это говорит мне о том, что DI по какой-то причине не работает, когда я запустить тесты. Любые идеи? Вот мой тест:

public function testXYZShouldDoTheThing()
{
    $some_controller = new SomeController();
    $some_controller->doThisOtherThing(...args...);
    ...asserts...
}

Я пробовал такие вещи, как использование методов bind и make в приложении в методе setUp, но безуспешно:

public function setUp(): void
{
    parent::setUp();
    $this->app->make('App\Repositories\XYZRepository');
}

Ответы [ 2 ]

1 голос
/ 04 марта 2020

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

Что вы можете сделать, это разрешить контроллер через сервисный контейнер:

public function testXYZShouldDoTheThing()
{
    $controller = $this->app->make(SomeController::class);
    // Or use the global resolve helper
    $controller = resolve(SomeController::class);

    $some_controller->doThisOtherThing(...args...);
    ...asserts...
}

Из документов :

You может использовать метод make для разрешения экземпляра класса из контейнера. Метод make принимает имя класса или интерфейса, для разрешения которого вы будете использовать sh:

$api = $this->app->make('HelpSpot\API');

Если вы находитесь в месте, где у вашего кода нет доступа к Переменная $ app, вы можете использовать помощника глобального разрешения:

$api = resolve('HelpSpot\API');

PS:

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

Функциональные тесты могут проверять большую часть вашего кода, включая то, как несколько объектов взаимодействуют с каждым другой или даже полный HTTP-запрос к JSON конечной точке.

примерно так:

use Illuminate\Http\Response;

public function testXYZShouldDoTheThing()
{
    $this->get('your/route')
        ->assertStatus(Response::HTTP_OK);
        // assert response content is correct (assertJson etc.)
}
0 голосов
/ 03 марта 2020

Это верно. Вся идея модульного теста заключается в том, что вы высмеиваете зависимые сервисы, чтобы вы могли последовательно контролировать их ввод / вывод.

Вы можете создать фиктивную версию своего XYZRepository и вставить ее в свой контроллер.

$xyzRepositoryMock = $this->createMock(XYZRepository::class);
$some_controller = new SomeController($xyzRepositoryMock);
$some_controller->doThisOtherThing(...args...);
...