I'm running Laravel, when trying to develop automated there's an issue. I have the following Test:
class SubmitSurvivorTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
//Test for submit with missing parameters
public function testEmptySubmit()
{
$response = $this->get('/submit/survivor');
$response->assertSee('422');
}
//Test for submit with complete parameters
public function testCorrectSubmit()
{
$response = $this->get('/submit/survivor?name=Jhon+Doe&age=22&gender=m&latitude=0&longitude=0&water=6&food=4&medicine=9&ammo=4');
$response->assertSee('666');
}
}
The first one should return true since if i go to /submit/survivor I'll get
{"status":"422","message":"The request could not be delivered since it's missing one or more paramters"}
The second one should return false since I get
{"status":"204","message":"Created successfully"}
But still the second one returns true aswell. No matter the value i put into the second assertSee running phpunit always gives me 0 failures. That is true for every other test aswell, it seems to me that it's only validating the first method.
I've tried changing it to
public function testEmptySubmit()
{
$response = $this->get('/submit/survivor');
$response->assertSee('422');
$asd = $this->get('/submit/survivor?name=Jhon+Doe&age=22&gender=m&latitude=0&longitude=0&water=6&food=4&medicine=9&ammo=4');
$asd->assertSee('666');
}
And still no failures, but if i change the value in the first one I still get a failure.
Changing the order didn't seem to have any impact, I changed the testCorrectSubmit to above the testEmptySubmit and it still wouldn't work.
If i change it to assertDontSee in the second one it gives a failure, even tho it should return true, the error is too large to even fit into my terminal but is something along the lines of
[A ton of lines with html,js and stuff i can't identify]
</script>\n
</body>\n
</html>\n
' does not contain "666".
C:\xampp\htdocs\mysurvivorapi\vendor\laravel\framework\src\Illuminate\Foundation\Testing\TestResponse.php:331
C:\xampp\htdocs\mysurvivorapi\tests\Feature\submitSurvivorTest.php:27
FAILURES!
Tests: 6, Assertions: 6, Failures: 1.
I can't even see the "Failed to assert that" but i just assume it's there
Using assertSeeText seems just as weird since
public function testCorrectSubmit()
{
$response = $this->get('/submit/survivor?name=Jhon+Doe&age=22&gender=m&latitude=0&longitude=0&water=6&food=4&medicine=9&ammo=4');
$response->assertSeeText('Jhon');
}
returns no erros, but if i change 'Jhon' to '666' it returns an error even tho neither of them are in the page
According to the documentation it should look like
public function testCorrectSubmit()
{
$response = $this->get('/submit/survivor',[
'name' => 'Jhon Doe',
'age' => '22',
'gender' => 'm',
'latitude' => '0',
'longitude' => '0',
'water' => '6',
'food'=>'4',
'medicine' =>'9',
'ammo' => '4']);
$response->assertSeeText('Jhon');
}
which works partially, it returns
Failed asserting that '{"status":"422","message":"The request could not be delivered since it's missing one or more paramters"}' contains "Jhon".
which shows me that it's making the request but it's not sending the GET parameters.
I did everything up to step 3 from #Spacemudd, since I have no idea how to do step 4, now it is sending the GET parameters since now Jhon Doe appears in the DB. But now the phpunit is returning
C:\xampp\htdocs\mysurvivorapi>php vendor/phpunit/phpunit/phpunit
PHPUnit 7.0.2 by Sebastian Bergmann and contributors.
.{"status":"204","message":"Created successfully"}
and that's it, no failures no ok no nothing, but if I go to the survivors table I can see that Jhon Doe was inserted and I guess that's an improvement?
Turns out that it was working as intended. The last weird behaviour was due to the fact that I had other tests that were just as wrong as the testCorrectSubmit. Removing them solved the issue, and now I'll rewrite the correctly.
Turns out that I was wrong again, it seems that even with the correct structure and no other tests testing the complete submit fails. I deleted all my tests, created a new one using php artisan make:test submitSurvivorTest which looks like this
<?php
namespace Tests\Feature;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
class submitSurvivorTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function testSubmitEmpty()
{
$route = route('submit.survivor');
$response = $this->get($route);
$response->assertSee('422');
}
public function testSubmitComplete()
{
$route = route('submit.survivor', [
'name' => 'Jhon Doe',
'age' => '22',
'gender' => 'm',
'latitude' => '0',
'longitude' => '0',
'water' => '6',
'food'=> '4',
'medicine' => '9',
'ammo' => '4',
]);
$response = $this->get($route);
$response->assertStatus(200);
}
}
I've put names into my routes file and it looks like this:
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
//Go to Index
Route::get('/', 'Controller#goHome');
//Submit a new Survivor
Route::name('submit.survivor')->get('/submit/survivor', 'SurvivorsController#submit');
//See a survivor, if no survivor id is submited sees all registered survivors
Route::name('request.survivors')->get('/request/survivors', 'SurvivorsController#getSurvivors');
//See statistics from the survivors, like average water per survivor for example.
Route::name('request.survivors.reports')->get('/request/survivors/reports', 'SurvivorsController#getReportsSurvivors');
//Update a survivor location
Route::name('update.survivor.location')->get('/update/survivor/location', 'SurvivorsController#updateSurvivorLocation');
//Flag a survivor as contaminated
Route::name('update.survivor.flag')->get('/update/survivor/flag', 'LogsController#submitFlag');
//Trade items between survivors
Route::name('update.survivors.trade')->get('/update/survivor/trade', 'TradesController#submitTrade');
and my submit method is simply
public function submit(Request $request){
//Check if the required variables where sent, and if no it'll return an error
$validator = Validator::make($request->all(), [
'name' => 'required',
'age' => 'required',
'gender' => 'required',
'latitude' => 'required',
'longitude' => 'required',
'water' => 'required',
'food' => 'required',
'medicine' => 'required',
'ammo' => 'required'
]);
if ($validator->fails()) {
$result = array('status' => '422', 'message' => 'The request could not be delivered since it\'s missing one or more paramters');
echo json_encode($result);
}
//Create a new survivor
$survivor = new Survivor;
$survivor->name = $request->input('name');
$survivor->age = $request->input('age');
$survivor->gender = $request->input('gender');
$survivor->latitude = $request->input('latitude');
$survivor->longitude = $request->input('longitude');
$survivor->water = $request->input('water');
$survivor->food = $request->input('food');
$survivor->medicine = $request->input('medicine');
$survivor->ammunition = $request->input('ammo');
//Save survivor
$survivor->save();
//Set the success message
$result = array('status' => '204', 'message' => 'Created successfully');
//Save the change into the logs and display the result
app( 'App\Http\Controllers\LogsController')->submitLog($survivor->id,$survivor->id,"register",$result);
exit();
}
All the functions work directly into the browser, they just don't work with the automated tests.
As of this moment running returns
C:\xampp\htdocs\mysurvivorapi>php vendor/phpunit/phpunit/phpunit
PHPUnit 7.0.2 by Sebastian Bergmann and contributors.
..Array{"status":"204","message":"Created successfully"}
and by using --testdox-html it seems to me that it stops it's execution even before displaying the result of the first test, which works fine
It seems that running with --process-isolation avoids the crashing and the test performs just fine. I've changed all the echo $response to return, as it seems to be better that way. Now it validates the emptySubmit but doesn't validate the completeSubmit
By removing the exit() now the test runs without --process-isolation and returns
Failed asserting that '' contains "204".
C:\xampp\htdocs\mysurvivorapi\vendor\laravel\framework\src\Illuminate\Foundation\Testing\TestResponse.php:253
C:\xampp\htdocs\mysurvivorapi\tests\Feature\submitSurvivorTest.php:36
FAILURES!
Tests: 4, Assertions: 4, Failures: 1.
But was that was fixed by making it so the response is returned directly in the submit method and not in the log. Now it seems to be working properly as it now returns responses based on the validations. I'll develop the rest of the automated tests and see if the problem shows up again.
Make sure your larvel.log isn't showing anything out of the ordinary when you finish the test.
Be sure the route you are calling is not under the middleware auth in which case you have to authenticate the user before calling the route.
$response = $this->actingAs($user)->get('/submit/survivor');
Where $user could be an instance of your User model class.
Consider using the helper command route() to build the URL.
In your web.php, add a name to your route, e.g.
Route::name('survivors.submit')->get('survivors/submit', 'SurvivorsController#store');
In your test, it would become like:
$route = route('survivors.submit', [
'name' => 'Jhon Doe',
'age' => '22',
'gender' => 'm',
'latitude' => '0',
'longitude' => '0',
'water' => '6',
'food'=> '4',
'medicine' => '9',
'ammo' => '4',
]);
$response = $this->get($route);
Use dd(); in your submit controller/functions then run the test. This will help you to identify where exactly the problem is.
For example to make sure you are receiving the correct session data, you would call dd(request()->except('_token'));
Related
I run a customer order input through a simple form validation.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateOrderWebstoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
// Use additional Address valudation rules if Order is not picked up
// dd('debugging halt',request());
$address_rules = [];
if (! request()->is_pickup) {
$address_rules = [
'street' => 'required|min:3|max:100',
'house_number' => 'required|numeric|max:100000',
'house_number_suffix' => 'nullable|max:10',
'postal_code' => 'required|max:10',
'town' => 'required|min:3|max:50',
'country' => 'required|min:2|max:2',
'region' => 'nullable|min:3|max:50',
'owner' => 'nullable|min:3|max:25',
];
}
return array_merge([
'first_name' => 'required|min:3|max:50',
'middle_name' => 'nullable|min:1|max:15',
'last_name' => 'required|min:3|max:50',
'company_name' => 'nullable|min:3|max:50',
'email_address' => 'required|email:rfc,dns|max:50',
'telephone_number' => 'nullable|min:10|max:25',
'description' => 'nullable|min:5|max:250',
], $address_rules);
}
public function messages()
{
return [
];
}
}
The weird thing this has been working without customer having errors for months. But in de last week or so we suddenly got multiple customers complaining that the validation of their email failed (the first 'rule' that triggers the 'email' response from validation.php
The problem is that testing this on a local host we can't reproduce this, only on the live server.
The host is a shared host, dedicated for Laravel apps, currently running Laravel 6.18.15 and PHP 7.4.16. Local is running running Laravel 6.18.10 and PHP 7.4.2 We had some problems before where the host failed to update PHP, but that doesn't seem to be the case here (if even possible)
The problem is that I don't really know how to fix this or even circumvent it. Changing the email validation to
'email' => 'required|regex:/(.+)#(.+)\.(.+)/i'
is giving me an 'IDN Conversion Failed' error.
I currently have a controller, with its path declared like this:
Route::resource('/route_testing', 'RouteTestingController')->middleware('checkPermission:my_permission_index');
he accesses the store() method
and I have my test done like this:
public function testCreateExcessCarriers(){
$formData = [
'user_id' => 1233,
'agency_id' => 444,
'uuid' => \Uuid::generate(),
'email' => 'faker#faker.com',
'name' => 'faker name'
];
//$this->withoutExceptionHandling();
$this->post(route('route_testing.store'), $formData)
->assertStatus(200);
}
The problem I have is that in my controller the store () method is commented, I have commented to see if the error in the test jumps.
That the store() method in the controller is commented, that is to say as it would not exist but in the test if it gives OK, I understand it is because if you made the insert which I understand it cannot be true since it is commented. And if I try it from the front, it effectively jumps Status Code: 500 Internal Server Error. Shouldn't it be the same in the test?
$this->assertDatabaseHas() not working with JSON/JSONb columns.
So how can I tests these types of columns in Laravel?
Currently, I have a store action. How can I perform an assertion, that a specific column with pre-defined values was saved.
Something like
['options->language', 'en']
is NOT an option, cause I have an extensive JSON with meta stuff.
How can I check the JSON in DB at once?
UPD
Now can be done like that.
I have solved it with this one-liner (adjust it to your models/fields)
$this->assertEquals($store->settings, Store::find($store->id)->settings);
Laravel 7+
Not sure how far back this solution works.
I found out the solution. Ignore some of the data label, Everything is accessible, i was just play around with my tests to figure it out.
/**
* #test
*/
public function canUpdate()
{
$authUser = UserFactory::createDefault();
$this->actingAs($authUser);
$generator = GeneratorFactory::createDefault();
$request = [
'json_field_one' => [
'array-data',
['more-data' => 'cool'],
'data' => 'some-data',
'collection' => [
['key' => 'value'],
'data' => 'some-more-data'
],
],
'json_field_two' => [],
];
$response = $this->putJson("/api/generators/{$generator->id}", $request);
$response->assertOk();
$this->assertDatabaseHas('generators', [
'id' => $generator->id,
'generator_set_id' => $generator->generatorSet->id,
// Testing for json requires arrows for accessing the data
// For Collection data, you should use numbers to access the indexes
// Note: Mysql dose not guarantee array order if i recall. Dont quote me on that but i'm pretty sure i read that somewhere. But for testing this works
'json_field_one->0' => 'array-data',
'json_field_one->1->more-data' => 'cool',
// to access properties just arrow over to the property name
'json_field_one->data' => 'some-data',
'json_field_one->collection->data' => 'some-more-data',
// Nested Collection
'json_field_one->collection->0->key' => 'value',
// Janky way to test for empty array
// Not really testing for empty
// only that the 0 index is not set
'json_field_two->0' => null,
]);
}
Note: The below solution is tested on Laravel Version: 9.x and Postgres version: 12.x
and the solution might not work on lower version of laravel
There would be two condition to assert json column into database.
1. Object
Consider Object is in json column in database as shown below:
"properties" => "{"attributes":{"id":1}}"
It can assert as
$this->assertDatabaseHas("table_name",[
"properties->attributes->id"=>1
]);
2. Array
Consider array is in json column as shown below:
"properties" => "[{"id":1},{"id":2}]"
It can assert as
$this->assertDatabaseHas("table_name",[
"properties->0->id"=>1,
"properties->1->id"=>2,
]);
Using json_encode on the value worked for me:
$this->assertDatabaseHas('users', [
'name' => 'Gaurav',
'attributes' => json_encode([
'gender' => 'Male',
'nationality' => 'Indian',
]),
]);
I ran the following test and I am receiving a failed_asserting that false is true. Can someone further explain why this could be?
/** #test */
public function a_user_logs_in()
{
$user = factory(App\User::class)->create(['email' => 'john#example.com', 'password' => bcrypt('testpass123')]);
$this->visit(route('login'));
$this->type($user->email, 'email');
$this->type($user->password, 'password');
$this->press('Login');
$this->assertTrue(Auth::check());
$this->seePageIs(route('dashboard'));
}
Your PHPUnit test is a client, not the web application itself. Therefore Auth::check() shouldn't return true. Instead, you could check that you are on the right page after pressing the button and that you see some kind of confirmation text:
/** #test */
public function a_user_can_log_in()
{
$user = factory(App\User::class)->create([
'email' => 'john#example.com',
'password' => bcrypt('testpass123')
]);
$this->visit(route('login'))
->type($user->email, 'email')
->type('testpass123', 'password')
->press('Login')
->see('Successfully logged in')
->onPage('/dashboard');
}
I believe this is how most developers would do it. Even if Auth::check() worked – it would only mean a session variable is created, you would still have to test that you are properly redirected to the right page, etc.
In your test you can use your Model to get the user, and you can use ->be($user) so that it will get Authenticate.
So i written in my test case for API test
$user = new User(['name' => 'peak']);
$this->be($user)
->get('/api/v1/getManufacturer')
->seeJson([
'status' => true,
]);
it works for me
I am currently working on a custom module add-on and I wanted to be able to use sorting and filtering on the a table in my control panel admin. I am using the EE table class and form helper. I'm trying to follow the documentation here for setting it up, but when I call try to call the '_datasource' method in my class I get this error
Fatal error: Call to undefined method Content_publish::_datasource() in /home/public_html/system/expressionengine/libraries/EE_Table.php on line 162
I have a feeling it's a scoping issue, but in the table class '$this->EE->table->datasource()' method you are supposed to just pass a string value with the name of your datasource function which is what I'm doing.
I don't seem to be the only one with this issue. There are more details and code examples on this EE Discussion forum thread
The documentation is not really clear. I also tried looking at EE's own comments module to see if i could figure it out, but no luck. Anyone have experience with this?
Here is the method I'm calling:
$data = $this->EE->table->datasource('_datasource');
And this is my function in my class:
function _datasource()
{
// ....
// $query comes from DB result set code above.
// I have omitted it here for brevity
$datarows = array();
foreach ($query->result_array() as $key => $row)
{
$datarows[] = array(
'entry_id' => $row['entry_id'],
'date' => date('Y-m-d',$row['entry_date']),
'author' => $row['screen_name'],
'payment' => $payment_amount,
'status' => $status,
'title' => $edit_href.$row['title']."</a>"
);
}
return $datarows;
}
Your datasource callback function must be on your Module_mcp class (looking at your forum thread you are trying to use it on a plugin which would explain the error).
If you want to put the datasource method on a different class, then just add this line right before you call datasource() to trick the table library into using the correct class:
// ensure table callbacks use this class rather than our MCP file
$this->EE->_mcp_reference =& $this;
$data = $this->EE->table->datasource('_datasource');
The table and form_validation libraries are the only two which use the special _mcp_reference variable, so I can't see any side effects to changing it, and have successfully done this in at least two modules.
On a side note, if you want a good example of how to use the built in tablesorter, take a look at system/expressionengine/controllers/cp/members.php. The documentation is pretty bad, but the source code always tells the truth :)
I've been having issues too and have a mixed solution of generate() and datasource working. Here it is here:
In my mcp file:
public function index()
{
$this->EE->cp->set_variable('cp_page_title', lang('my_module_name'));
$data = $this->EE->table->datasource('_datasource');
return $this->EE->load->view('index', $data, TRUE);
}
public function _datasource()
{
$headers = array(
'name' => array('header' => 'Name'),
'color' => array('header' => 'Color'),
'size' => array('header' => 'Size')
);
$rows = array(
array('name' => 'Fred', 'color' => 'Blue', 'size' => 'Small'),
array('name' => 'Mary', 'color' => 'Red', 'size' => 'Large'),
array('name' => 'John', 'color' => 'Green', 'size' => 'Medium'),
);
return array(
'rows' => $rows,
'headers' => $headers
);
}
In my index view file:
$this->table->set_columns($headers);
$this->table->set_data($rows);
echo $this->table->generate();
Seems to be working at the moment and I've not tried pagination yet, but sorting works.