问题描述
我搜索了StackOverflow,然后网络尝试了我发现的所有内容,以解决以下问题,但无济于事。该错误仅在PHPunit测试期间发生。如果运行Laravel应用程序(PHP artisan服务),则可以正常运行。
以下是详细信息:
我正在运行Win7的cygwin。我使用的是在PHP artisan create项目中创建的composer.json。
Cygwin (win 7)
Runtime: PHP 7.4.8
PHPUnit 9.3.7 by Sebastian Bergmann and contributors.
Laravel Framework 7.26.0
这是我的composer.json,我认为它最初是由“ PHP artisan create project”或类似的东西创建的。
$ cat composer.json
{
"name": "rubens-gomes/rubens-gomes.com","type": "project","description": "rubens-gomes.com Web Site","homepage": "https://rubens-gomes.com","keywords": [
"framework","laravel"
],"license": "PROPRIETARY","authors": [
{
"name": "Rubens Gomes","email": "rubens.s.gomes@gmail.com","role": "Developer"
}
],"support": {
"email": "rubens.s.gomes@gmail.com"
},"require": {
"PHP": ">=7.4","fideloper/proxy": "^4.2","fruitcake/laravel-cors": "^2.0","google/recaptcha": "^1.2","guzzlehttp/guzzle": "^6.3","laravel/framework": "^7.24","laravel/tinker": "^2.0"
},"require-dev": {
"facade/ignition": "^2.0","fzaninotto/faker": "^1.9.1","mockery/mockery": "^1.3.1","nunomaduro/collision": "^4.1","PHPunit/PHPunit": "^9.2"
},"config": {
"optimize-autoloader": true,"preferred-install": "dist","sort-packages": true
},"extra": {
"laravel": {
"dont-discover": []
}
},"autoload": {
"psr-4": {
"App\\": "app/"
},"classmap": [
"database/seeds","database/factories"
]
},"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},"minimum-stability": "dev","prefer-stable": true,"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump","@PHP artisan package:discover --ansi"
],"post-root-package-install": [
"@PHP -r \"file_exists('.env') || copy('.env.example','.env');\""
],"post-create-project-cmd": [
"@PHP artisan key:generate --ansi"
],"post-update-cmd": "@composer dump-autoload"
}
}
下面是我的PHPunit.xml。我尝试了不同的引导程序,但无济于事。
<?xml version="1.0" encoding="UTF-8"?>
<PHPunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.PHPunit.de/9.3/PHPunit.xsd"
<!-- bootstrap="vendor/autoload.PHP" -->
bootstrap="bootstrap/app.PHP"
colors="true"
verbose="true">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".PHP">./app</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.PHP">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.PHP">./tests/Feature</directory>
</testsuite>
</testsuites>
<PHP>
<server name="APP_ENV" value="testing"/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="MAIL_MAILER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>
<server name="SESSION_DRIVER" value="array"/>
<server name="TELEScopE_ENABLED" value="false"/>
</PHP>
</PHPunit>
这是PHPunit致命错误。我不太担心下面的测试用例失败。主要问题是有关“未找到配置...”的致命错误
$ PHP artisan test
Warning: TTY mode is not supported on Windows platform.
PASS Tests\Unit\ExampleTest
✓ basic test
FAIL Tests\Unit\app\Http\Controllers\ContactControllerTest
✕ fail to validate email
Tests: 1 Failed,1 passed,1 pending
Failed asserting that exception of type "ErrorException" matches expected exception "Illuminate\Validation\ValidationException". Message was: "dns_get_record(): DNS Query Failed" at D:\gitprojs\PHP\rubens-gomes\vendor\egulias\email-validator\src\Validation\DNSCheckValidation.PHP:121 D:\gitprojs\PHP\rubens-gomes\vendor\egulias\email-validator\src\Validation\DNSCheckValidation.PHP:107 D:\gitprojs\PHP\rubens-gomes\vendor\egulias\email-validator\src\Validation\DNSCheckValidation.PHP:80 D:\gitprojs\PHP\rubens-gomes\vendor\egulias\email-validator\src\Validation\MultipleValidationWithAnd.PHP:65 D:\gitprojs\PHP\rubens-gomes\vendor\egulias\email-validator\src\EmailValidator.PHP:37 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Validation\Concerns\ValidatesAttributes.PHP:666 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Validation\Validator.PHP:547 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Validation\Validator.PHP:370 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Validation\Validator.PHP:401 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Validation\Validator.PHP:444 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Validation\Factory.PHP:136 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Foundation\Providers\FoundationServiceProvider.PHP:58 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Support\Traits\Macroable.PHP:111 D:\gitprojs\PHP\rubens-gomes\app\Http\Controllers\ContactController.PHP:90 D:\gitprojs\PHP\rubens-gomes\tests\Unit\app\Http\Controllers\ContactControllerTest.PHP:43 .
at D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\Constraint\Constraint.PHP:121
117| if (!empty($description)) {
118| $failureDescription = $description . "\n" . $failureDescription;
119| }
120|
> 121| throw new ExpectationFailedException(
122| $failureDescription,123| $comparisonFailure
124| );
125| }
1 D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\Constraint\Constraint.PHP:55
PHPUnit\Framework\Constraint\Constraint::fail(Object(ErrorException),"")
2 D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\Assert.PHP:2331
PHPUnit\Framework\Constraint\Constraint::evaluate(Object(ErrorException),"")
PHP Fatal error: Uncaught ReflectionException: Class config does not exist in D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Container\Container.PHP:809
Stack trace:
#0 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Container\Container.PHP(809): ReflectionClass->__construct('config')
#1 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Container\Container.PHP(691): Illuminate\Container\Container->build('config')
#2 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Foundation\Application.PHP(796): Illuminate\Container\Container->resolve('config',Array,true)
#3 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Container\Container.PHP(637): Illuminate\Foundation\Application->resolve('config',Array)
#4 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Foundation\Application.PHP(781): Illuminate\Container\Container->make('config',Array)
#5 D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\C in D:\gitprojs\PHP\rubens-gomes\vendor\laravel\framework\src\Illuminate\Container\Container.PHP on line 811
PHP Stack trace:
PHP 1. {main}() D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\PHPunit:0
PHP 2. PHPUnit\TextUI\Command::main($exit = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\PHPunit:61
PHP 3. PHPUnit\TextUI\Command->run($argv = *uninitialized*,$exit = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\TextUI\Command.PHP:100
PHP 4. PHPUnit\TextUI\TestRunner->run($suite = *uninitialized*,$arguments = *uninitialized*,$warnings = *uninitialized*,$exit = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\TextUI\Command.PHP:147
PHP 5. PHPUnit\Framework\TestSuite->run($result = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\TextUI\TestRunner.PHP:671
PHP 6. PHPUnit\Framework\TestSuite->run($result = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\TestSuite.PHP:665
PHP 7. PHPUnit\Framework\TestSuite->run($result = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\TestSuite.PHP:665
PHP 8. Tests\Unit\app\Http\Controllers\ContactControllerTest->run($result = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\TestSuite.PHP:665
PHP 9. PHPUnit\Framework\TestResult->run($test = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\TestCase.PHP:880
PHP 10. PHPUnit\Framework\TestResult->addFailure($test = *uninitialized*,$e = *uninitialized*,$time = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\TestResult.PHP:894
PHP 11. NunoMaduro\Collision\Adapters\PHPunit\Printer->addFailure($testCase = *uninitialized*,$error = *uninitialized*,$time = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\PHPunit\PHPunit\src\Framework\TestResult.PHP:391
PHP 12. NunoMaduro\Collision\Adapters\PHPunit\Style->writeError($state = *uninitialized*,$throwable = *uninitialized*) D:\gitprojs\PHP\rubens-gomes\vendor\nunomaduro\collision\src\Adapters\PHPunit\PrinterContents.PHP:114
下面是正在测试的控制器。注意,这是非常基本的代码,它只是为了检查事情是否正常进行测试。
顺便说一句,我正在学习Laravel。
$ cat app/Http/Controllers/ContactController.PHP
<?PHP
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Facades\Log;
use ReCaptcha\ReCaptcha;
/**
* Controller to handle incoming "/contact" form requests.
*
* @author Rubens Gomes <rubens.s.gomes@gmail.com>
* @copyright 2020 Rubens Gomes
*/
final class ContactController extends Controller
{
/** @var array containing fields to be used on the view.*/
private $data;
/** @var array contact form validation rules. */
private $formRules;
/**
*
* @var \ReCaptcha\ReCaptcha instance of Google ReCaptcha to be injected
* during unit testing. If no instance is injected a new one is created.
*/
private $recaptcha;
/**
* The default constructor.
*
* @param ReCaptcha $rc The Google ReCaptcha object.
*/
public function __construct(ReCaptcha $rc = null)
{
if ($rc === null) {
$this->recaptcha = new ReCaptcha(config('services.recaptcha.secret_key'));
} else {
$this->recaptcha = $rc;
}
$this->data = [
'pageId' => 'contact','homeHref' => '/#home','aboutHref' => '/#about','contactHref' => '#contact'
];
$this->formRules = [
'email' => 'required|email:dns','name' => 'required','msg' => 'required','g-recaptcha-response' => 'required'
];
Log::debug(__CLASS__ . " constructed");
}
public function __destruct()
{
Log::debug(__CLASS__ . " destructed");
}
/**
* Process the incoming GET request to render the contact page.
*
* @return \Illuminate\View\View The contact main page view.
*/
public function index()
{
Log::debug(__CLASS__ . ":" . __FUNCTION__);
return view('contact',$this->data);
}
/**
* Handles the contact message form POST submit by validating the form
* fields,validating the captcha and then sending an email message with
* the information provided in the form.
*
* @param \Illuminate\Http\Request $request
* The HTTP request
* @return \Illuminate\View\View The contact main page view.
*/
public function sendmsg(Request $request)
{
Log::debug(__CLASS__ . ":" . __FUNCTION__);
// validate input contact fields
Log::debug('validating contact form');
$request->validate($this->formRules);
// validate the captcha
$captcha = $request->input(config('services.recaptcha.resp_parm'));
Log::debug('validating captcha...');
$response = $this->recaptcha->verify($captcha);
if (! $response->isSuccess()) {
Log::warning('Failed to authenticate captcha');
$captchaError = new MessageBag();
$captchaError->add('recaptcha_error','Failed to authenticate captcha');
return view('contact',$this->data)->withErrors($captchaError);
}
return redirect()->back()->withSuccess('Your message has been successfully submitted.');
}
}
/******************************************************************************/
// EOF
这是我的控制器测试代码:
$ cat tests/Unit/app/Http/Controllers/ContactControllerTest.PHP
<?PHP
namespace Tests\Unit\app\Http\Controllers;
use App\Http\Controllers\ContactController;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Tests\TestCase;
use ReCaptcha\ReCaptcha;
class ContactControllerTest extends TestCase
{
/** @var \App\Http\Controllers\ContactController The controller being tested */
private $controller;
/** @var ReCaptcha\ReCaptcha The Google ReCaptcha being doubled */
private $rcStub;
public function setUp(): void
{
parent::setUp();
$this->rcStub = $this->createStub(ReCaptcha::class);
$this->controller = new ContactController($this->rcStub);
}
public function tearDown(): void
{
parent::tearDown();
}
public function testFailTovalidateEmail()
{
$this->expectException(ValidationException::class);
$form = [
'email' => 'rubens.s.gomes @ gmail.com','msg' => 'This is a test.\nRubens Gomes','name' => 'Rubens+Gomes','g-recaptcha-response' => 'hello google captcha'
];
$request = Request::create('/contact/sendmsg','POST',$form);
$this->controller->sendmsg($request);
}
}
这是我的.env文件:
$ cat .env
APP_NAME="Laravel"
APP_ENV="local"
APP_KEY="base64:mJmYLlc2V43AKfa3u7s1po7RHyPDajRZIB4pIArTyjg="
APP_DEBUG=true
APP_URL="http://localhost"
LOG_CHANNEL="stack"
DB_CONNECTION="MysqL"
DB_HOST="127.0.0.1"
DB_PORT=3306
DB_DATABASE="laravel"
DB_USERNAME="root"
DB_PASSWORD="root"
broADCAST_DRIVER="log"
CACHE_DRIVER="file"
QUEUE_CONNECTION="sync"
SESSION_DRIVER="file"
SESSION_LIFETIME=120
REdis_HOST="127.0.0.1"
REdis_PASSWORD="null"
REdis_PORT=6379
MAIL_MAILER="smtp"
MAIL_HOST="smtp.mailtrap.io"
MAIL_PORT=2525
MAIL_USERNAME="null"
MAIL_PASSWORD="null"
MAIL_ENCRYPTION="null"
MAIL_FROM_ADDRESS="null"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
AWS_DEFAULT_REGION="us-east-1"
AWS_BUCKET=""
PUSHER_APP_ID=""
PUSHER_APP_KEY=""
PUSHER_APP_SECRET=""
PUSHER_APP_CLUSTER="mt1"
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
RECAPTCHA_DOMAIN="NOT_disPLAYED"
RECAPTCHA_SITE_KEY="NOT_disPLAYED"
RECAPTCHA_SECRET_KEY="NOT_disPLAYED"
RECAPTCHA_OWNER="rubens.s.gomes@gmail.com"
RECAPTCHA_VERSION="V2"
解决方法
我重写了上面的所有代码。现在工作正常。以下是一些新代码的一部分。
composer.json:
$ cat composer.json
{
"name" : "rubens-gomes/rubens-gomes.com","type" : "project","description" : "rubens-gomes.com Web Site","homepage" : "https://rubens-gomes.com","keywords" : [
"framework","laravel"
],"license" : "PROPRIETARY","authors" : [{
"name" : "Rubens Gomes","email" : "rubens.s.gomes@gmail.com","role" : "Developer"
}
],"support" : {
"email" : "rubens.s.gomes@gmail.com"
},"require" : {
"php" : ">=7.4","fideloper/proxy" : "^4.2","fruitcake/laravel-cors" : "^2.0","google/recaptcha" : "^1.2","guzzlehttp/guzzle" : "^6.3","laravel/framework" : "^7.24","laravel/tinker" : "^2.0"
},"require-dev" : {
"facade/ignition" : "^2.0","fzaninotto/faker" : "^1.9.1","mockery/mockery" : "^1.3.1","nunomaduro/collision" : "^4.1","phpunit/phpunit" : "^8.5"
},"config" : {
"optimize-autoloader" : true,"preferred-install" : "dist","sort-packages" : true
},"extra" : {
"laravel" : {}
},"autoload" : {
"psr-4" : {
"App\\" : "app/"
},"classmap" : [
"database/seeds","database/factories"
]
},"autoload-dev" : {
"psr-4" : {
"Tests\\" : "tests/"
}
},"minimum-stability" : "dev","prefer-stable" : true,"scripts" : {
"post-autoload-dump" : [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump","@php artisan package:discover --ansi"
],"post-root-package-install" : "@php -r \"file_exists('.env') || copy('.env.example','.env');\"","post-create-project-cmd" : "@php artisan key:generate --ansi","post-update-cmd" : "@composer dump-autoload"
}
}
phpunit.xml:
$ cat phpunit.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
To run the unit tests configured in this PHPUnit XML configuration file:
php artisan test -vvv
@author Rubens Gomes <rubens.s.gomes@gmail.com>
@link https://rubens-gomes.com
-->
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
bootstrap="bootstrap/app.php"
colors="true"
verbose="true">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<php>
<server name="APP_ENV" value="testing"/>
</php>
</phpunit>
.env:
$ cat .env
APP_NAME="rubens-gomes.com"
APP_ENV="testing"
APP_KEY="base64:mJmYLlc2V43AKfa3u7s1po7RHyPDajRZIB4pIArTyjg="
APP_DEBUG=true
APP_URL="http://localhost:8080"
LOG_CHANNEL="stdout"
MAIL_MAILER="smtp"
MAIL_HOST="smtp.gmail.com"
MAIL_PORT=465
MAIL_USERNAME="NOT_DISPLAYED"
MAIL_PASSWORD="NOT_DISPLAYED"
MAIL_FROM_ADDRESS="NOT_DISPLAYED"
MAIL_FROM_NAME="${APP_NAME}"
RECAPTCHA_DOMAIN="rubens-gomes.com"
RECAPTCHA_SITE_KEY="NOD_DISPLAYED"
RECAPTCHA_SECRET_KEY="NOT_DISPLAYED"
RECAPTCHA_OWNER="rubens.s.gomes@gmail.com"
RECAPTCHA_VERSION="V2"
测试输出:
$ php artisan test -vvv
Warning: TTY mode is not supported on Windows platform.
PASS Tests\Unit\ExampleTest
✓ basic test
RUNS Tests\Unit\app\Http\Controllers\ContactControllerTest
• fail due to invalid form
Tests: 4 passed,17 pending
[2020-08-30T05:29:18.173039+00:00] testing.DEBUG: creating contact instance [] []
RUNS Tests\Unit\app\Http\Controllers\ContactControllerTest
• ensure sucess response for valid form
PASS Tests\Unit\app\Http\Controllers\ContactControllerTest
✓ ensure request to index page works
✓ fails due to wrong resource
✓ fail due to post method not allowed for index page
✓ fail due to invalid form
✓ ensure sucess response for valid form
PASS Tests\Unit\app\Http\Controllers\HomeControllerTest
✓ ensure request to index page works
✓ fails due to wrong resource
PASS Tests\Unit\app\Mail\ContactRequestMailableTest
✓ fail due to null contact
✓ ensure constructor works
✓ ensure build works
PASS Tests\Unit\app\Mail\ContactResponseMailableTest
✓ fail due to null contact
✓ ensure constructor works
✓ ensure build works
RUNS Tests\Unit\app\Model\ContactTest
• fail due to invalid email
RUNS Tests\Unit\app\Model\ContactTest
• fail due to invalid email domain
PASS Tests\Unit\app\Model\ContactTest
✓ fail due to invalid email
✓ fail due to invalid email domain
✓ ensure contact is created
✓ ensure email is not empty
✓ ensure name is not empty
✓ ensure msg is not empty
PASS Tests\Feature\ExampleTest
✓ basic test
Tests: 21 passed
Time: 4.93s
ContactControllerTest.php:
<?php
namespace Tests\Unit\app\Http\Controllers;
use App\Http\Controllers\ContactController;
use App\Mail\ContactRequestMailable;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use ReCaptcha\ReCaptcha;
use Tests\TestCase;
class ContactControllerTest extends TestCase
{
/** @var \App\Http\Controllers\ContactController The controller being tested */
private $controller;
/** @var ReCaptcha\ReCaptcha The Google ReCaptcha being doubled */
private $rcStub;
/** @var array valid post form data for testing purposes */
private const VALID_FORM = [
'email' => 'rubens.s.gomes@gmail.com','msg' => 'This is a test.\nRubens Gomes','name' => 'Rubens+Gomes','g-recaptcha-response' => 'hello google captcha'
];
/** @var array invalid post form data for testing purposes */
private const INVALID_FORM = [
'email' => 'rubens.s.gomes@gmail.com','name' => '','g-recaptcha-response' => 'hello google captcha'
];
public function setUp(): void
{
parent::setUp();
$this->rcStub = $this->createStub(ReCaptcha::class);
$this->controller = new ContactController($this->rcStub,Mail::fake());
}
public function tearDown(): void
{
parent::tearDown();
}
public function testEnsureRequestToIndexPageWorks()
{
$response = $this->get('/contact/');
$this->assertTrue($response->isOk());
}
public function testFailsDueToWrongResource()
{
$response = $this->get('/contact/hello');
$response->assertStatus(404);
}
public function testFailDueToPostMethodNotAllowedForIndexPage()
{
$response = $this->post('/contact/');
$response->assertStatus(405);
}
public function testFailDueToInvalidForm()
{
$response = $this->post('/contact/sendmsg',self::INVALID_FORM);
$response->assertRedirect();
}
// unit testing controller
public function testEnsureSucessResponseForValidForm()
{
$request = Request::create('/contact/sendmsg','POST',self::VALID_FORM);
$rcResp = new \ReCaptcha\Response(TRUE);
$this->rcStub->method('verify')->willReturn($rcResp);
$this->controller->sendmsg($request);
Mail::assertSent(ContactRequestMailable::class,1);
}
}
ContactController.php
<?php
namespace App\Http\Controllers;
use App\Mail\ContactRequestMailable;
use App\Model\Contact;
use Illuminate\Http\Request;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use ReCaptcha\ReCaptcha;
/**
* Controller to handle incoming "/contact" form requests.
*
* @author Rubens Gomes <rubens.s.gomes@gmail.com>
*/
final class ContactController extends Controller
{
/** @var array containing fields to be used in the view.*/
private const VIEW_DATA = [
'pageId' => 'contact','homeHref' => '/#home','aboutHref' => '/#about','contactHref' => '#contact'
];
/** @var \Illuminate\Support\Facades\Mail instance of mail service. */
private $mailer;
/** @var \ReCaptcha\ReCaptcha instance of Google ReCaptcha. */
private $recaptcha;
/**
* The default constructor.
*
* @param ReCaptcha $rc
* The Google ReCaptcha object.
*/
public function __construct(ReCaptcha $rc = null)
{
if ($rc === null) {
$this->recaptcha = new ReCaptcha(config('services.recaptcha.secret_key'));
} else {
// used during unit testing to inject a test double object
$this->recaptcha = $rc;
}
}
/**
* Process the incoming GET request to render the contact page.
*
* @return \Illuminate\View\View The contact main page view.
*/
public function index()
{
return view('contact',self::VIEW_DATA);
}
/**
* Handles the contact message form POST submit by validating the form
* fields,validating the captcha and then sending an email message with
* the information provided in the form.
*
* @param \Illuminate\Http\Request $request
* The HTTP request
* @return \Illuminate\View\View The contact main page view.
*/
public function sendmsg(Request $request)
{
// validate and create contact
$data = [
'name' => $request->input('name'),'email' => $request->input('email'),'msg' => $request->input('msg')
];
$contact = null;
try {
Log::debug('creating contact instance');
$contact = new Contact($data['name'],$data['email'],$data['msg']);
} catch (\InvalidArgumentException $ex) {
Log::error('failed to create contact',$data);
return redirect()->back()
->withException($ex)
->withErrors($ex->getMessage());
}
// validate the captcha
$captcha = $request->input(config('services.recaptcha.resp_parm'));
Log::debug('validating captcha',['captcha' => $captcha]);
$response = $this->recaptcha->verify($captcha);
if (! $response->isSuccess()) {
Log::warning('failed to authenticate captcha',['captcha' => $captcha]);
$captchaError = new MessageBag();
$captchaError->add('recaptcha_error','Failed to authenticate captcha');
return redirect()->back()->withErrors($captchaError);
}
// send message by email
Log::debug('sending email',['email' => $contact->getEmail() ]);
$reqMail = new ContactRequestMailable($contact);
Mail::to($contact->getEmail())->send($reqMail);
return redirect()->back()->withSuccess('Your message has been successfully submitted.');
}
}
/******************************************************************************/
// EOF
Contact.php:
<?php
namespace App\Model;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
/**
* A data model object to represent a contact (e.g.,from the contact form).
*
* @author Rubens Gomes <rubens.s.gomes@gmail.com>
*/
class Contact
{
/** @var array $validationRules The rules for validating input data */
private static $validationRules = [
'name' => 'required|max:100','email' => 'required|email:rfc,dns','msg' => 'required|max:1000',];
/** @var string $name The contact name */
private $name;
/** @var string $email The contact email address */
private $email;
/** @var string $msg The contact message */
private $msg;
/**
* Default constructor.
*
* @param string $name
* The non-blank contact name.
* @param string $email
* The non-blank contact email.
* @param string $msg
* The non-blank contact message.
* @throws \InvalidArgumentException if any of the arguments is not valid.
*/
public function __construct($name,$email,$msg)
{
$data = [
'name' => $name,'email' => $email,'msg' => $msg
];
$validator = Validator::make($data,self::$validationRules);
if($validator->fails()) {
Log::error("name or email invalid",$data);
throw new \InvalidArgumentException("Invalid argument: " . $validator->errors());
}
$this->name = $name;
$this->email = $email;
$this->msg = $msg;
}
public function getEmail() : string
{
return $this->email;
}
public function getMessage() : string
{
return $this->msg;
}
public function getName() : string
{
return $this->name;
}
}
/******************************************************************************/
// EOF
ContactRequestMailable.php:
<?php
namespace App\Mail;
use App\Model\Contact;
use Illuminate\Mail\Mailable;
/**
* Mailable used to send email of message in contact form.
*
* @author Rubens Gomes
*/
class ContactRequestMailable extends Mailable
{
private const SUBJECT = "Rubens Gomes - Contact";
/** @var Contact The contact instance. */
private $contact;
/**
* Default constructor
*
* @param Contact $contact
* the non-null and valid contact instance.
* @throws \InvalidArgumentException if any of the arguments is not valid.
*/
public function __construct($contact = null)
{
if (is_null($contact)) {
throw new \InvalidArgumentException("contact cannot be null.");
}
$this->contact = $contact;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->subject(self::SUBJECT)
->locale('en')
->from(config('mail.from.address'),config('mail.from.name'))
->to(config('mail.from.address'),config('mail.from.name'))
->view('emails.contact-request')
->with([
'name' => $this->contact->getName(),'email' => $this->contact->getEmail(),'msg' => $this->contact->getMessage()
]);
}
}
/******************************************************************************/
// EOF```