Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing #52

Open
pardmeister opened this issue Jan 19, 2023 · 6 comments
Open

Testing #52

pardmeister opened this issue Jan 19, 2023 · 6 comments

Comments

@pardmeister
Copy link

Hi

I'm beginning to use this in a project, and it's quite intuitive so far, however, I was wondering how one would go about writing tests for a wizard that's written in this package.

@michaelklopf
Copy link

Hey,

we test two parts. The individual steps and the onCompleteAction.

When the wizard completes it runs this code

        $result = $this->actionResolver
            ->resolveAction($this->onCompleteAction)
            ->execute($this->transformWizardData($request));

So we have an action that has the following signature:

<?php

namespace /* namespace */;

/* imports here */

final class SaveMyObject extends WizardAction
{
    public function __construct(
        public DoSomethingAction $doSomethingAction,
        public DoMoreAction $doMoreAction
    ) {
    }

    public function execute(mixed $payload): ActionResult
    {
        $object_you_want = DB::transaction(function () use ($payload) {
            $object_you_want = $this->doSomethingAction->execute($payload); // our own actions, we adapt the techniques from Laravel Beyond Crud
            $this->doMoreAction->execute($payload);

            return $object_you_want;
        });

        return $this->success(['object_id' => $object_you_want->id]);
    }
}

Now in the test you can mock your actions and check they are called and you get returned what you expect

    /** @test */
    public function it_creates_my_object()
    {
        Storage::fake('minio');

        $payload = [
            'a_date' => '2021-01-01',
            'attribute_value' => 'foo',
            'stuff' => 'bar',
            'an_array' => [
                'content' => '<p>Example content</p>',
            ],
        ];

        $this->mock(DoSomethingAction::class, function (MockInterface $mock) {
            $mock->shouldReceive('execute')
                ->once()->andReturn(MyObject::factory()->create());
        });

        $this->mock(DoMoreAction::class, function (MockInterface $mock) {
            $mock->shouldReceive('execute')
                ->once();
        });

        $this->action = app(SaveMyObject::class);
        $result = $this->action->execute($payload);

        $this->assertInstanceOf(ActionResult::class, $result);

        $my_object = MyObject::first();

        $this->assertEquals($result->get('object_id'), $my_object->id);
    }

Or you simply let the code run through and assert everything you want here. The payload is not necessary in this example but I adapted from our code and didn't want to change everything.

And then there is testing individual steps. In the setup you create an instance of the wizard. We assume this is step 3 of many.

    protected function setUp(): void
    {
        parent::setUp();

        $this->wizardId = Wizard::create([
            'class' => MyObjectWizard::class,
            'data' => [
                'important_value' => AnotherObject::factory()->create()->id,
                '_arcanist' => [
                    'step_1' => true,
                    'step_2' => true,
                ],
            ],
        ])->id;

        Passport::actingAs(User::factory()->wizardOfOz()->create());
    }

And in the test you can assert if the wizard uses the correct fields.

    /** @test */
    public function returning_the_correct_fields()
    {
        $fields = $this->get(
            route('wizard.my-object.show', [
                'wizardId' => $this->wizardId,
                'slug' => 'type_value',
            ])
        )->json('fields');

        $used_fields = collect($fields)->mapWithKeys(fn ($field) => [
            $field['name'] => $field['component'],
        ])->all();

        $this->assertEquals(
            [
                'a_date' => 'Date',
                'attribute_value' => 'Text',
                'something_else' => 'Select',
                'stuff' => 'Text',
                'money' => 'Currency',
                'an_array' => 'CheckboxGroup',
            ],
            $used_fields
        );
    }

Or if the validation is correct

        $this->withExceptionHandling()
            ->postJson(
                route('wizard.my-object.update', [
                    'wizardId' => $this->wizardId,
                    'slug' => 'type_value',
                ]),
                [ 'stuff' => null ]
            )
            ->assertStatus(422)
            ->assertJsonValidationErrors('stuff');

I don't know if this helps, but maybe someone needs a starting point or seeing it done wrong 😬

@samarjaffal
Copy link

Hi, I have problems when I try to make a test for the wizard. Do you have other examples? I want to test a Wizard that I had created and the steps for the wizard. I'll appreciate the help on this. Thanks

@michaelklopf
Copy link

Well, you can post your code with markdown code tags here and we can see if we can help you. I have no other kind of test. I either test the completion of the wizard or the steps as described above.

@samarjaffal
Copy link

The problem is when I test step with your code, it's giving me 404 not found.
here->> $this->get(route('route_name'));

But if I check if the route exists, the result is true. So, I don't know if I'm missing something.

@michaelklopf
Copy link

I can duplicate the error, when I use a wizardId that doesn't exist in the route helper.

        $fields = $this->get(
            route('wizard.my-object.show', [
                'wizardId' => '20000',
                'slug' => 'slug_value',
            ])
        )->json('fields');

So maybe

        $this->wizardId = Wizard::create([
            'class' => MyObjectWizard::class,
            'data' => [
                'important_value' => AnotherObject::factory()->create()->id,
                '_arcanist' => [
                    'slug_value_1' => true,
                    'slug_value_2' => true,
                ],
            ],
        ])->id;

didn't return the correct wizard for you?

Namespace use Arcanist\Repository\Wizard; is correct for the Wizard? You need to pay attention to the slug_value values in the _arcanist part. They say that the previous steps are done. In the first step the _arcanist array can be left out.

                '_arcanist' => [
                    'step_1' => true,
                    'step_2' => true,
                ],

Maybe try for a simple check if this works, because when setting up the test you have only one wizard in the db which has ID 1.

        $fields = $this->get(
            route('wizard.my-object.show', [
                'wizardId' => '1',
                'slug' => 'slug_value',
            ])
        )->json('fields');

Oh, and another thing that could be a problem is if the use RefreshDatabase; trait is missing on the test case.

Other stuff I wrote first:

Check if the route helper is returning the wrong route. Either dump() it and check against php artisan route:list or instead of the route helper call the route directly with

$fields = $this->get(
            '/api/arcanist/wizards/my-object/'.$this->wizardId.'/type_value'
        )->json('fields')

The part api/arcanist/wizards is taken from the arcanist config

    'route_prefix' => 'api/assign/wizards',

404 error usually means that something went wrong in the test with calling the correct route or the setup of the app.

@samarjaffal
Copy link

Sorry, I didn't answer your reply. I just want to tell you that I did get it work! Thanks a lot for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants