mewebstudio / captcha

Captcha for Laravel 5/6/7/8/9/10/11
MIT License
2.45k stars 453 forks source link

How to get captcha code in test ? #237

Open FzSalehi opened 2 years ago

FzSalehi commented 2 years ago

Hi , im using this captcha in a form and i want to do some tests .. how to get correct code and insert it in form to submit the form?

sinamiandashti commented 2 years ago

same question

Sergej-Tihonov commented 2 years ago

@FzSalehi , @sinamiandashti I had the same problem today. Here my solution for it. Not pretty, but does the work. :stuck_out_tongue_winking_eye:

Some explanation:

You can not get the captcha value, because it is hashed after the creation and only the hash is obtained. See: https://github.com/mewebstudio/captcha/blob/master/src/Captcha.php#L343

The comparison is done via hashing and comparing the provided value with existing hash. See: https://github.com/mewebstudio/captcha/blob/master/src/Captcha.php#L478

The good thing is, that the author used dependency injection and all the properties and methods are public or protected, so you can easily override necessary parts and replace the base class. :thumbsup:

Here my simple Fake Class solution:

<?php

namespace Tests;

use Mews\Captcha\Captcha;

class CaptchaFake extends Captcha
{
    public static string $expectedValue = '';

    public static function fake(string $value = ''): void
    {
        self::$expectedValue = $value;

        app()->bind('captcha', function ($app) {
            return new CaptchaFake(
                $app['Illuminate\Filesystem\Filesystem'],
                $app['Illuminate\Contracts\Config\Repository'],
                $app['Intervention\Image\ImageManager'],
                $app['Illuminate\Session\Store'],
                $app['Illuminate\Hashing\BcryptHasher'],
                $app['Illuminate\Support\Str']
            );
        });
    }

    public function check(string $value): bool
    {
        return self::$expectedValue === $value;
    }
}

I have added a static expected value property, so I can easily simulate success and failure. Just call the fake method at the beginning of your test method:

CaptchaFake::fake('123456');

I did not wanted to mock a lot so I just copied the creation of the original Captcha Class from the provider. See: https://github.com/mewebstudio/captcha/blob/master/src/CaptchaServiceProvider.php#L73

If you use the check_api method, you can fake it as well. https://github.com/mewebstudio/captcha/blob/master/src/Captcha.php#L505

Hope it helps you as well. Enjoy :wink:

ronaldaraujo commented 2 years ago

Oohh man, @Sergej-Tihonov. Thank so much. It's work!

FzSalehi commented 2 years ago

@sinamiandashti thank you for your help .. I have also a solution which is not so pretty too 😜 but it is simpler

you can modify laravel request validations .. right ?

so just put a simple condition like so:

// in some controller
public function store(Request $request)
{
    //here we decide which inputs required
    $requiredInputs = $this->getInputRequired(); 

    //then do the validation ...
    $validatedData = $request->validate($requiredInputs );

    // The blog post is valid...
}

private function getInputRequired(){
    // this is golden condition
    if(env('debug')){

       return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ];

    return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'captcha' => 'required',
    ];
    }

}

in this way .. you will never need to pass captcha input from your test requests.

But anyways I suggest to @mewebstudio to do something about the testing environment.

I think @Sergej-Tihonov 's solution could be implemented in this package.

m3di commented 2 years ago

expectedValue

it's a very bad practice, never mix test and production code.

rezasrk commented 2 years ago

Hi You can use it like below

If used API

Captcha::shouldReceive('check_api')->andReturn(true);

If did not use API

Captcha::shouldReceive('check')->andReturn(true);

@sinamiandashti @FzSalehi

githubcodehome commented 1 year ago

Hi You can use it like below

If used API

Captcha::shouldReceive('check_api')->andReturn(true);

If did not use API

Captcha::shouldReceive('check')->andReturn(true);

@sinamiandashti @FzSalehi

thank you, this work for me, I use FormRequest validation and it's working