My objective is to ensure that an object graph has expected values and types. I want to ensure every value is of the expected type.
To this end, assertEquals() is unfortunately not useful:
$this->assertEquals(
[ 'prop' => '0' ],
[ 'prop' => 0 ]
);
// -> no failures
In this case, assertSame() works nicely:
$this->assertSame(
[ 'prop' => '0' ],
[ 'prop' => 0 ]
);
// Failed asserting that Array &0 (
// 'prop' => 0
// ) is identical to Array &0 (
// 'prop' => '0'
// ).
The problem with assertSame() is that it also checks reference for objects:
$this->assertSame(
(object) [ 'prop' => 0 ],
(object) [ 'prop' => 0 ]
);
// Failed asserting that two variables reference the same object.
What options do I have?
On a separate note, I'm not sure why this was designed in this way - to me it feels that assertSame() does two things at once (I've have at most verified object class, not references).
So far, I came up with the following option:
/**
* #param mixed $expected
* #param mixed $actual
* #param string $message
*/
public function assertObjectGraph($expected, $actual, $message = '')
{
$expected = $this->convertObjectsToHashes($expected);
$actual = $this->convertObjectsToHashes($actual);
$this->assertSame($expected, $actual, $message);
}
/**
* #param mixed $value
* #return mixed
*/
private function convertObjectsToHashes($value)
{
if (is_object($value)) {
$value = ['__CLASS__' => get_class($value)] + get_object_vars($value);
}
if (is_array($value)) {
$value = array_map([$this, __FUNCTION__], $value);
}
return $value;
}
Examples:
$this->assertObjectGraph(
(object) [ 'prop' => 0 ],
(object) [ 'prop' => 0 ]
);
// -> ok
$this->assertObjectGraph(
(object) [ 'prop' => 0 ],
(object) [ 'prop' => '0' ],
);
// Failed asserting that Array &0 (
// '__CLASS__' => 'stdClass'
// 'prop' => '0'
// ) is identical to Array &0 (
// '__CLASS__' => 'stdClass'
// 'prop' => 0
// ).
class Test{public $prop = 0;}
$this->assertObjectGraph(
(object) [ 'prop' => 0 ],
new Test()
);
// Failed asserting that Array &0 (
// '__CLASS__' => 'Test'
// 'prop' => 0
// ) is identical to Array &0 (
// '__CLASS__' => 'stdClass'
// 'prop' => 0
// ).
You could first check for array key existence then check for correct type, as example:
$this->assertArrayHasKey('prop', $input);
$this->assertInternalType(\PHPUnit_Framework_Constraint_IsType::TYPE_INT, $input['prop']);
// or more simply as this->assertTrue(is_int($new_id));
$this->assertEquals(0, $input['prop']);
hope this help
As your aim seems to be to compare 2 arrays,
it should be noted that in official PHP manual at - Array Operators - there is described support of === operator for arrays
$a === $b Identity TRUE if $a and $b have the same key/value pairs in
the same order and of the same types. // same types is only for values - does not check key types
So what would you do is to compare result of this in a true assert:
$this->assertTrue(['a' => '1', 'b' => '2'] === ['a' => 1, 'b' => '2']);
assertEquals() fails in your case because it does exactly what it says, it tests with == - an equal operator which I admit is not really intuitive and brings some funky stuff such as
$this->assertEquals(321, true); // this would PASS!
As for objects, you should define a whole separate comparator.
Related
I'm currently stuck on a problem I can't figure out on a wordpress side project. Here's the thing :
I made a plugin that queries an API, and processes its data to insert posts.
The problem I face is that, when the main function is triggered manually (i.e. via a route or cron manually launched) posts are correctly created or updated, but when the cron executed automatically, posts are always duplicated. I may have narrowed the problem down to capabilities (I check if posts exists by querying the cpt for a unique meta-field), and when the cron executes on its own, it find no ids.
Here are code samples :
public function insertPosts() {
set_current_user(1); // <- here I tried setting the user as admin for the script, but it didn't work
// First, get all prestations from the api
$prestations = $this->api->getPrestations();
/*
* Then :
* we remove sessions of formations initiales, which are useless (gamme id 10 = formations initiales)
* I could have "only" taken the ids I need, but unsetting useless array entries allows for a lighter dataset
*/
$prestations_info = [];
foreach ($prestations as $key => $prestation) {
if ($prestation['gamme']['id'] == 10) {
// Removes the useless $prestation and breaks out of the loop so its ID's not stored
unset($prestations[$key]);
continue;
}
$prestations_info[] = [
'fid' => $prestation['id'],
'cat_name' => $prestation['gamme']['name'],
'cat_id' => $prestation['gamme']['id']
];
}
/*
* Then :
* Parse the array to process every id individually and query the api for core informations for the singles
*/
foreach ($prestations_info as $presta) {
// Meta query to check if a post with the formation's id already exists
$args = [
'post_type' => 'formations',
'posts_per_page' => -1,
'meta_query' => [
[
'key' => 'api_fid',
'value' => $presta['fid'],
'compare' => '='
]
]
];
$posts = new WP_Query($args);
if ($posts->have_posts()) {
while ($posts->have_posts()) {
// If a post with that formation id already exists, we retrieve its ID:
// this way, we can update existing post and avoid duplicates
$posts->the_post();
$post_id = get_the_ID();
}
}
$body = $this->api->getSessionsByPrestationsId($presta['fid']);
$post_array = [];
$locations = [];
$nb_places = [];
foreach ($body as $key => $item) {
if (!empty($item['seances'])) {
$duration = BOUtilities::getFormationDuration($item['seances']);
$dates = BOUtilities::getDatesPerSessions($item['seances']);
}
if ($dates) {
$session_date = [];
if (is_array($dates)) {
foreach ($dates as $i => $date) {
$session_date[$i] = [
'dates' => $date['start'] .' - ' . $date['end'],
];
}
} else {
$session_date[] = [
'dates' => $dates,
];
}
}
if (!empty($item['location'])) {
$locations[] = strtolower($item['location']);
$session[] = [
'sessions_location' => $item['location'],
'sessions_dates' => $session_date,
];
}
if (!empty($item['nbplace']) && $item['nbplace'] != 0) {
if (!in_array($item['nbplace'], $nb_places)) {
$nb_places[] = $item['nbplace'];
}
}
if ($item['price'] != 0) {
$prices[] = $item['price'];
}
}
$formatted_cost = BOUtilities::setPrices($prices);
$effectifs = BOUtilities::setEffectifs($nb_places);
if ($post_id) {
$post_array = [
'ID' => $post_id,
'post_type' => 'formations',
'post_title' => $item['prestation']['name'],
'meta_input' => [
'api_fid' => $presta['fid'],
'aside_cost' => $formatted_cost,
'aside_duration' => ($duration > 1 ? $duration . ' jours' : $duration . ' jour'),
'aside_effectifs' => $effectifs,
]
];
$inserted_post = wp_update_post($post_array);
}
else {
$post_array = [
'post_type' => 'formations',
'post_title' => $item['prestation']['name'],
'meta_input' => [
'api_fid' => $presta['fid'],
'aside_cost' => $formatted_cost,
'aside_duration' => ($duration > 1 ? $duration . ' jours' : $duration . ' jour'),
'aside_effectifs' => $effectifs,
]
];
$inserted_post = wp_insert_post($post_array);
}
// The cron does not have the capability of adding taxonomy terms, they need to be be added separately
$inserted_terms = wp_set_object_terms($inserted_post, $presta['cat_name'], 'categories_formations');
$inserted_terms = wp_set_object_terms($inserted_post, $locations, 'ville');
update_field('aside_sessions', $session, $inserted_post);
unset($dates);
unset($session);
unset($formatted_cost);
unset($nb_places);
unset($prices);
}
}
This is very much work in progress, but, for now, I am stumped.
Why did Extbase throws an exception if my start date field is empty. If the date has the wrong format, the validation works. But an empty value shows this:
Uncaught TYPO3 Exception ... Events::setEnd() must be an instance of DateTime, null given ...
What is wrong with the following code in my model?
/**
* start
*
* #var \DateTime
* #validate NotEmpty
* #validate(type="DateTime", options={"locale"="de_DE"})
*/
protected $start;
And here is my TCA
'start' => [
'exclude' => false,
'label' => 'Start',
'config' => [
'type' => 'input',
'renderType' => 'inputDateTime',
'size' => 10,
'eval' => 'datetime',
'default' => time()
],
],
Cache cleared, typo3temp folder deleted.
UPDATE:
Perhaps the error is in my initializeUpdateAction() where i have to set the date format?
public function initializeUpdateAction() {
$user = $this->request->getArgument('feUsers');
$events = $user['events'];
if( is_array($events) ) {
foreach ($events as $i => $event) {
$this->arguments->getArgument('feUsers')
->getPropertyMappingConfiguration()->forProperty('events.'.$i.'.start')
->setTypeConverterOption(
'TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\DateTimeConverter',
\TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::CONFIGURATION_DATE_FORMAT,
'd.m.Y'
);
}
}
}
enhance your validation:
'eval' => 'datetime,int',
an empty date field is represented with 0, which is no valid date format.
I am facing this error in laravel 5.
Here is the controller function in which I am facing the error (in the line where I do ($vendor->client[0]->id) :
public function show($username) {
Log::info('Vendors Controller : show function with '.$username);
$vendor = VendorProfile::where('username', $username)->first();
$output = print_r($vendor,1);
Log::info($output);
if($vendor) {
Log::info('client '. $vendor->client);
$client = Client::find($vendor->client[0]->id);
$title = $client->profile->company_name;
$output is printed as:
[2015-06-15 21:34:43] local.INFO: App\models\VendorProfile Object
(
[table:protected] => vendor_profile
[guarded:protected] => Array
(
[0] => id
)
[morphClass:protected] => MorphVendorProfile
[connection:protected] =>
[primaryKey:protected] => id
[perPage:protected] => 15
[incrementing] => 1
[timestamps] => 1
[attributes:protected] => Array
(
[id] => 16
[first_name] => some name
[last_name] =>
[company_name] => some name
[contact_number] => 1234567890
[username] => username
[profile_photo] =>
[photo_mime_type] =>
[cover_photo] =>
[cover_photo_mime_type] =>
[address] =>
[city_id] => 1
[zip_code] =>
[story] =>
[establishment_date] =>
[pricing] =>
[education] =>
[services_offered] =>
[assignments_undertook] =>
[advanced_fees] =>
[equipments] =>
[about_service] =>
[coins] => 500
[created_at] => 2015-06-15 20:21:45
[updated_at] => 2015-06-15 20:21:45
)
[original:protected] => Array
(
[id] => 16
[first_name] => some name
[last_name] =>
[company_name] => some name
[contact_number] => 1234567890
[username] => username
[profile_photo] =>
[photo_mime_type] =>
[cover_photo] =>
[cover_photo_mime_type] =>
[address] =>
[city_id] => 1
[zip_code] =>
[story] =>
[establishment_date] =>
[pricing] =>
[education] =>
[services_offered] =>
[assignments_undertook] =>
[advanced_fees] =>
[equipments] =>
[about_service] =>
[coins] => 500
[created_at] => 2015-06-15 20:21:45
[updated_at] => 2015-06-15 20:21:45
)
[relations:protected] => Array
(
)
[hidden:protected] => Array
(
)
[visible:protected] => Array
(
)
[appends:protected] => Array
(
)
[fillable:protected] => Array
(
)
[dates:protected] => Array
(
)
[casts:protected] => Array
(
)
[touches:protected] => Array
(
)
[observables:protected] => Array
(
)
[with:protected] => Array
(
)
[exists] => 1
)
The models VendorProfile and Client are connected as:
in VendorProfile model:
protected $morphClass = 'MorphVendorProfile';
// Defining 'Polymorphic' Relationship with Client Model
public function client() {
return $this->morphMany('App\models\Client', 'profile');
}
and I have an alias in my config/app.php:
'MorphVendorProfile'=> 'App\models\VendorProfile'
in Client model :
public function profile() {
return $this->morphTo();
}
Update:
This error has occurred while migrating the code from laravel 4.2 to laravel 5. So right now, when I run the previous code that was based on 4.2 version with the SAME database, and it didn't throw me any error, so I think problem is with the code, not database. I am certain that there is a problem with 'morph' relationships, I had to modify a bit in the process of migrating to make it work on other pages.
here is the morphTo function in my Eloquent/Model.php:
/**
* Define a polymorphic, inverse one-to-one or many relationship.
*
* #param string $name
* #param string $type
* #param string $id
* #return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function morphTo($name = null, $type = null, $id = null)
{
// If no name is provided, we will use the backtrace to get the function name
// since that is most likely the name of the polymorphic interface. We can
// use that to get both the class and foreign key that will be utilized.
if (is_null($name))
{
list(, $caller) = debug_backtrace(false, 2);
$name = snake_case($caller['function']);
}
list($type, $id) = $this->getMorphs($name, $type, $id);
// If the type value is null it is probably safe to assume we're eager loading
// the relationship. When that is the case we will pass in a dummy query as
// there are multiple types in the morph and we can't use single queries.
if (is_null($class = $this->$type))
{
Log::info('eagerly loading');
return new MorphTo(
$this->newQuery(), $this, $id, null, $type, $name
);
}
// If we are not eager loading the relationship we will essentially treat this
// as a belongs-to style relationship since morph-to extends that class and
// we will pass in the appropriate values so that it behaves as expected.
else
{
Log::info('not eagerly loading');
Log::info($class);
$instance = \App::make('\App\models\\'.$class);
$output = print_r($instance,1);
Log::info('*'.$output.'*');
return new MorphTo(
$instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
);
}
}
Check your namespaces. Make sure that the polymorphic relationship that you have setup references the full namespace in both the Vendor and Client models
I created a parameter in my parameters.yml file:
parameters:
category:
var: test
How can I access this parameter in my config.yml? For example, I want to pass this parameter to all my twig files as a global twig variable:
twig:
globals:
my_var: %category.var% # throws ParameterNotFoundException
In all the symfony config files I've seen, the entries under 'parameters:' have always been fully qualified. I don't fully understand why this is but it may help you to write the entries in your parameters.yml like this:
category1.var1: xxx
category1.var2: yyy
category1.var3. zzz
category2.subcategory1.var1: 5
category2.subcategory1.var2: 10
category2.subcategory2.var1: foo
category2.subcategory2.var2: bar
... and so on.
EDIT
I tried pasting the nested parameter from the question into parameters.local.yml in one of my projects and ran a trivial unit test to retrieve that and a fully qualified parameter from the container e.g.
$testUserEmail = $container->getParameter('test.user.email');
$this->assertEquals('dahlia.flower#randomtest.com', $testUserEmail);
$testParam = $container->getParameter('category.var');
$this->assertEquals('test', $testParam);
The fully qualified parameter was fine, attempting to get the nested parameter resulted in an InvalidArgumentException: The parameter category.var must be defined. I don't think parameters can be defined with nesting.
$this->container->getParameter('category')['var']
I have tested that on symfony 2.8 and it worked for me.
Or you can write simple ParameterBag which can implement nested parameters dynamically (without config values duplicity):
<?php
namespace Your\Namespace;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
class ParameterBagNested extends ParameterBag
{
/**
* wire $this->set() logic into add() too
*
* #param array $parameters
*/
public function add( array $parameters )
{
foreach ( $parameters as $name => $value ) {
$this->set( $name, $value );
}
}
/**
* sets all levels of nested array parameters with dot notation
* - loggly[host: loggly.com] will be translated this way:
* - loggly: [host: loggly.com] - standard array parameter will be left as is
* - loggly.host: loggly.com - nested variables ar translated so you can access them directly too as parent.variable
*
* #param string $name
* #param mixed $value
*/
public function set( $name, $value )
{
if ( $this->has( $name ) ) {
// this is required because of array values
// we can have arrays defined there, so we need to remove them first
// otherwise some subvalues would to remain in the system and as a result, arrays would be merged, not overwriten by set()
$this->remove( $name );
}
$this->setNested( $name, $value );
}
/**
* remove checks even if name is not array
*
* #param string $name
*/
public function remove( $name )
{
$value = $this->get( $name );
if ( is_array( $value ) ) {
foreach ( $value as $k => $v ) {
$this->remove( $name . '.' . $k, $v );
}
}
if ( strpos( $name, '.' ) !== FALSE ) {
$parts = explode( '.', $name );
$nameTopLevel = reset( $parts );
array_shift( $parts );
$topLevelData = $this->removeKeyByAddress( $this->get( $nameTopLevel ), $parts );
ksort( $topLevelData );
$this->setNested( $nameTopLevel, $topLevelData );
}
parent::remove( $name );
}
/**
* #param array $data
* #param array $addressParts
*
* #return array
*/
private function removeKeyByAddress( $data, $addressParts )
{
$updatedLevel = & $data;
$i = 1;
foreach ( $addressParts as $part ) {
if ( $i === count( $addressParts ) ) {
unset( $updatedLevel[$part] );
} else {
$updatedLevel = & $updatedLevel[$part];
$i++;
}
}
return $data;
}
/**
* #see set()
*
* #param string $name
* #param mixed $value
*/
private function setNested( $name, $value )
{
if ( is_array( $value ) ) {
foreach ( $value as $k => $v ) {
$this->setNested( $name . '.' . $k, $v );
}
}
parent::set( $name, $value );
}
}
phpunit Test:
<?php
namespace Your\Namespace;
use Symfony\Component\DependencyInjection\Tests\ParameterBag\ParameterBagTest;
/**
* its essential to use ParameterBagNested as ParameterBag because this way we run even parent class tests upon it
* parent class is part of Symfony DIC standard test suite and we use it here just for check if our parameter bag is still ok
*/
use SBKS\DependencyInjection\ParameterBag\ParameterBagNested as ParameterBag;
/**
* testing basic and even added ParameterBag functionality
*/
class ParameterBagNestedTest extends ParameterBagTest
{
public function testConstructorNested()
{
$bag = new ParameterBag(
array(
'foo' => array( 'foo1' => 'foo' ),
'bar' => 'bar',
)
);
$this->assertEquals(
array(
'foo.foo1' => 'foo',
'foo' => array(
'foo1' => 'foo',
),
'bar' => 'bar',
),
$bag->all(),
'__construct() takes an array of parameters as its first argument'
);
}
public function testRemoveNested()
{
$bag = new ParameterBag(
array(
'foo' => array(
'foo1' => array(
'foo11' => 'foo',
'foo12' => 'foo',
),
'foo2' => 'foo',
),
'bar' => 'bar',
)
);
$bag->remove( 'foo.foo1.foo11' );
$this->assertEquals(
array(
'foo' => array(
'foo1' => array(
'foo12' => 'foo',
),
'foo2' => 'foo',
),
'foo.foo1' => array( 'foo12' => 'foo' ),
'foo.foo1.foo12' => 'foo',
'foo.foo2' => 'foo',
'bar' => 'bar',
),
$bag->all(),
'->remove() removes a parameter'
);
$bag->remove( 'foo' );
$this->assertEquals(
array(
'bar' => 'bar',
),
$bag->all(),
'->remove() removes a parameter'
);
}
public function testSetNested()
{
$bag = new ParameterBag(
array(
'foo' => array(
'foo1' => array(
'foo11' => 'foo',
'foo12' => 'foo',
),
'foo2' => 'foo',
),
)
);
$bag->set( 'foo', 'foo' );
$this->assertEquals( array( 'foo' => 'foo' ), $bag->all(), '->set() sets the value of a new parameter' );
}
public function testHasNested()
{
$bag = new ParameterBag(
array(
'foo' => array(
'foo1' => array(
'foo11' => 'foo',
'foo12' => 'foo',
),
'foo2' => 'foo',
),
)
);
$this->assertTrue( $bag->has( 'foo' ), '->has() returns true if a parameter is defined' );
$this->assertTrue( $bag->has( 'foo.foo1' ), '->has() returns true if a parameter is defined' );
$this->assertTrue( $bag->has( 'foo.foo1.foo12' ), '->has() returns true if a parameter is defined' );
$this->assertTrue( $bag->has( 'foo.foo2' ), '->has() returns true if a parameter is defined' );
}
}
Then you can use it by injecting Parameter bag into ContainerBuilder:
$parameterBag = new \Your\Namespace\ParameterBagNested();
$container = new ContainerBuilder($parameterBag);
Thats all, you can use nested parameter with dot notation now in Symfony DI container.
If you are using Symfony plugin in phpstorm it will autocomplete your nested attributes with dot notation too.
A bit late, but here is a solution that worked for me.
# app/config/config.yml
twig:
globals:
my_var: category['var']
Explanation
When you import your parameters file into your app/config/config.yml file, you can automatically access all variables defined in your parameters file.
Keep in mind, when you use the structure:
# app/config/parameters.yml
parameters:
category:
var: test
You are defining a parameter called category with a value that is in turn itself a key-value pair array that contains var as key and test as value.
Update
In newer versions of symfony (3.4 - 4.2) you can now do the following:
# app/config/config.yml
twig:
globals:
custom_categories: '%categories%'
By having this parameter set up:
# app/config/parameters.yml
parameters:
categories:
- category_A
- category_B
Things to note here:
- in parameters.yml categories is an array
so to use it in a twig template, you should be able to do something like:
<ul>
{% for category in categories %}
<li> {{ category }} </li>
{% endfor %}
</ul>
Another example with objects:
# app/config/parameters.yml
parameters:
marketplace:
name: 'My Store'
address: '...'
Configuring twig variables:
# app/config/config.yml
twig:
globals:
marketplace: '%marketplace%'
Using it in twig:
...
<p>{{marketplace.name}}</p>
<p>{{marketplace.address}}</p>
...
Hope this helps! :)
Symfony's expression language syntax to the rescue! Give this a shot:
twig:
globals:
my_var: '#=parameter("category")["var"]'
To provide an alternative approach that doesn't require removing values from the setter, I override the getter method instead.
namespace NameSpaceFor\ParameterBags;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
class ParameterBagParser extends ParameterBag
{
/**
* {#inheritDoc}
*/
public function get($name, $parent = null)
{
if (null === $parent) {
$parent = $this->parameters;
}
$name = strtolower($name);
if (!array_key_exists($name, $parent)) {
if (!$name) {
throw new ParameterNotFoundException($name);
}
if (false !== strpos($name, '.')) {
$parts = explode('.', $name);
$key = array_shift($parts);
if (isset($parent[$key])) {
return $this->get(implode('.', $parts), $parent[$key]);
}
}
$alternatives = [];
foreach ($parent as $key => $parameterValue) {
$lev = levenshtein($name, $key);
if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) {
$alternatives[] = $key;
}
}
throw new ParameterNotFoundException($name, null, null, null, $alternatives);
}
return $parent[$name];
}
}
It recursively traverses through the name until it is all of the dot notations have been checked.
So it will work with arrays and scalar values.
config.yml:
parameters:
my_param:
- test
my_inherited: '%my_param.0%' #test
ContainerAware:
$container->getParameter('my_param')[0]; //test
Trick to emulate nested parameters, acccess in the yaml file:
parameters:
crawler:
urls:
a: '%crawler.urls.a%' # here the root of the rest of the tree/array
crawler.urls.a:
cat1:
- aa
- bb
cat2:
- cc
services:
xx:
class: myclass
arguments:
$urls: '%crawler.urls.a%'
In Symfony I now access the parameter('crawler') as complete tree, in service xx the subtree/array is accessible.
I have to test an array with inner arrays.
my array looks like the following.
$testdata=Array
(
[0] => Array
(
[label] => 'Ammy'
[idr] => 'user7'
[rel] => 7
)
[1] => Array
(
[label] => 'sidh'
[idr] => user8
[rel] => 8
)
[2] => Array
(
[label] => 'Alan'
[idr] => 'user9'
[rel] => 9
)
)
in this case my requirement is to assert whether the keys for inner array present using assertArrayHasKey() assertion of phpunit. I tried to do it like this
foreach ($testdata as $values) {
//print_r($values);
$this->assertArrayHasKey('idr', $values);
$this->assertArrayHasKey('rel', $values);
}
but this is not working for me. even the control does not go inside the foreach() loop.
please suggest me some solution for this.
foreach ($testdata as $values) {
//print_r($values);
$this->assertArrayHasKey('idr', $values);
$this->assertArrayHasKey('rel', $values);
}
this part in my question works fine. actually i was not getting the array itself in the test scenario. so it was not going inside the foreach(). now it is solved. i had a mistake in passing args to the function.
This is the example usage
/** Example One */
$testData = [
[
'label' => '',
'idr' => ''
], [
'label' => '',
'idr' => ''
], [
'label' => '',
'idr' => ''
]
];
$this->assertArrayStructure([
['label','idr']
], $testData);
/** Example Two */
$testData = [
'result' => true,
'data' => [
'col_1' => '',
'col_2' => ''
],
];
$this->assertArrayStructure([
'result', 'data' => ['col_1', 'col_2']
], $testData);
/** Example Three */
$testData = [
'result' => true,
'data' => [
[
'col_1' => '',
'col_2' => ''
],
[
'col_1' => '',
'col_2' => ''
]
],
];
$this->assertArrayStructure([
'result', 'data' => ['col_1', 'col_2']
], $testData, true);
Here is function
/**
* Like as assertJsonStructure
*
* #param array $data
* #param array $structures #e.g., [ key_1, key_2 => [child_key]]
* #param bool $dataHasMultiArray #e.g., $data[0][key]
*/
protected function assertArrayStructure(array $structures, array $data, bool $dataHasMultiArray = false)
{
$i = 0;
foreach ($structures as $index => $key) {
if (!is_numeric($index)) {
$this->assertArrayHasKey($index, $data);
}
if (is_string($key)) {
$this->assertArrayHasKey($key, $data);
}
if (is_array($key)) {
$this->assertArrayHasKeys($key, $dataHasMultiArray ? $data[$index][$i] : $data[$index]);
$i++;
}
}
}
/**
* #param $structures
* #param array $data
*/
protected function assertArrayHasKeys($structures, array $data)
{
foreach ($structures as $key) {
$this->assertArrayHasKey($key, $data);
}
}
You can also use
assertArraySubset()
from: https://phpunit.de/manual/current/en/appendixes.assertions.html#appendixes.assertions.assertArraySubset
another solution is to compare arrays and then check if true:
$arrays_are_equal = ($array1 == $array2); // or === if you want identical
$this->assertTrue($arrays_are_equal);
Another option is to test only the first element of the multidimensional Array, like this:
$this->assertArrayHasKey('idr', $testdata[0]);
$this->assertArrayHasKey('rel', $testdata[0]);
I believe that that control is not going inside the foreach loop.
Remove whole :
$testdata= Array
(
[0] => Array
(
[label] => 'Ammy'
[idr] => 'user7'
[rel] => 7
)
[1] => Array
(
[label] => 'sidh'
[idr] => user8
[rel] => 8
)
[2] => Array
(
[label] => 'Alan'
[idr] => 'user9'
[rel] => 9
)
)