Test contents of return array in PHPSpec - testing

Say I have this method of a RuleFactory:
public function makeFromArray($rules)
{
$array = [];
foreach ($rules as $rule) {
$array[] = new Rule($rule[0], $rule[1]);
}
return $array;
}
I want to test that the return array contains Rule elements. Here is my test:
function it_should_create_multiple_rules_at_once()
{
$rules = [
['required', 'Please provide your first name'],
['alpha', 'Please provide a valid first name']
];
$this->makeFromArray($rules)->shouldHaveCount(2);
$this->makeFromArray($rules)[0]->shouldBeInstanceOf('Rule');
$this->makeFromArray($rules)[1]->shouldBeInstanceOf('Rule');
}
But this does not work, it throws an error in PHPSpec.
The strange thing is that I can do this just fine on other methods that return arrays, but for some reason I cannot do that here.
The error I get is this:
! it should create multiple rules at once
method [array:2] not found
How do I test the contents of this return array, WITHOUT creating my own inline matcher?

Your method accepts a single rule, not all of them. The spec should be:
$this->makeFromArray($rules)->shouldHaveCount(2);
$this->makeFromArray($rules[0])[0]->shouldBeAnInstanceOf('Rule');
$this->makeFromArray($rules[1])[1]->shouldBeAnInstanceOf('Rule');
Or, to avoid multiple calls:
$rules = $this->makeFromArray($rules);
$rules->shouldHaveCount(2);
$rules[0]->shouldBeAnInstanceOf('Rule');
$rules[1]->shouldBeAnInstanceOf('Rule');
Still, the most readable version would be the one leveraging a custom matcher:
$rules->shouldHaveCount(2);
$rules->shouldContainOnlyInstancesOf('Rule');

Related

Testing that an array of objects contains a certain key using Mocha/Chai

I am trying to write some tests using Mocha and Chai on an array of objects. I want to go through this array and return any objects that contain the error key. I figured this sort of thing would be easy, but I am having a difficult time of it.
[
{ fileName: 'font1.ttc', error: 'font_type_not_supported' },
{ fileName: 'font2.ttf', error: 'parse_failed' },
{ fileName: 'font3.tff' }
]
I've tried things like.
expect(testResult).to.have.nested.property('error');
I'm probably missing something simple, does anyone have any suggestions?
I think you can use node function filter to do your work.
Something like this works:
var array = [
{ fileName: 'font1.ttc', error: 'font_type_not_supported' },
{ fileName: 'font2.ttf', error: 'parse_failed' },
{ fileName: 'font3.tff' }
]
var filter = array.filter(item => item.error != undefined)
With this way you get the elements where exists the attribute.
But if you want to compare how many elements has the property, you can use length attribute.
var count = array.filter(item => item.error != undefined).length
expect(count).gt(0)
If count variable is greater than 0, implies that at least one object has the attribute.

Nested arrays to call map and shorten repeated maps for single object omit property

I have a situation here where I want to omit the nested maps and get it done in one liner . Can it be done using chain or any other ways .
self.workorder.tasklist = _.map(self.workorder.tasklists, function (tasklist) {
tasklist.tasklistGroups = _.map(tasklist.tasklistGroups, function (tasklistGroup, tgKey) {
tasklistGroup.tasklistItems = _.map(tasklistGroup.tasklistItems, function (taskListItem, tKey) {
taskListItem = _.omit(taskListItem, ["open"]);
return taskListItem;
});
return tasklistGroup;
});
return tasklist;
});
I don't want so many nested map calls .
Because you are modifying your items in place I would say this is possible:
_.chain(self.workorder.tasklists).map(function(tasklist) {
return tasklist.tasklistGroups;
}).flatten().map(function(group) {
return group.tasklistItems
}).flatten().forEach(function(item) {
delete item.open;
}).value();
Jsfiddle
The idea is to flatten your array to last level (level of items) and then modify them using forEach.

Why does this documentation example fail? Is my workaround an acceptable equivalent?

While exploring the documented example raised in this perl6 question that was asked here recently, I found that the final implementation option - (my interpretation of the example is that it provides three different ways to do something) - doesn't work. Running this;
class HTTP::Header does Associative {
has %!fields handles <iterator list kv keys values>;
sub normalize-key ($key) { $key.subst(/\w+/, *.tc, :g) }
method EXISTS-KEY ($key) { %!fields{normalize-key $key}:exists }
method DELETE-KEY ($key) { %!fields{normalize-key $key}:delete }
method push (*#_) { %!fields.push: #_ }
multi method AT-KEY (::?CLASS:D: $key) is rw {
my $element := %!fields{normalize-key $key};
Proxy.new(
FETCH => method () { $element },
STORE => method ($value) {
$element = do given $value».split(/',' \s+/).flat {
when 1 { .[0] } # a single value is stored as a string
default { .Array } # multiple values are stored as an array
}
}
);
}
}
my $header = HTTP::Header.new;
say $header.WHAT; #-> (Header)
$header<Accept> = "text/plain";
$header{'Accept-' X~ <Charset Encoding Language>} = <utf-8 gzip en>;
$header.push('Accept-Language' => "fr"); # like .push on a Hash
say $header<Accept-Language>.perl; #-> $["en", "fr"]
... produces the expected output. Note that the third last line with the X meta-operator assigns a literal list (built with angle brackets) to a hash slice (given a flexible definition of "hash"). My understanding is this results in three seperate calls to method AT-KEY each with a single string argument (apart from self) and therefore does not exersise the default clause of the given statement. Is that correct?
When I invent a use case that excersises that part of the code, it appears to fail;
... as above ...
$header<Accept> = "text/plain";
$header{'Accept-' X~ <Charset Encoding Language>} = <utf-8 gzip en>;
$header{'Accept-Language'} = "en, fr, cz";
say $header<Accept-Language>.perl; #-> ["en", "fr", "cz"] ??
# outputs
(Header)
This Seq has already been iterated, and its values consumed
(you might solve this by adding .cache on usages of the Seq, or
by assigning the Seq into an array)
in block at ./hhorig.pl line 20
in method <anon> at ./hhorig.pl line 18
in block <unit> at ./hhorig.pl line 32
The error message provides an awesome explanation - the topic is a sequence produced by the split and is now spent and hence can't be referenced in the when and/or default clauses.
Have I correctly "lifted" and implemented the example? Is my invented use case of several language codes in the one string wrong or is the example code wrong/out-of-date? I say out-of-date as my recollection is that Seq's came along pretty late in the perl6 development process - so perhaps, this code used to work but doesn't now. Can anyone clarify/confirm?
Finally, taking the error message into account, the following code appears to solve the problem;
... as above ...
STORE => method ($value) {
my #values = $value».split(/',' \s+/) ;
$element = do given #values.flat {
when 1 { $value } # a single value is stored as a string
default { #values } # multiple values are stored as an array
}
}
... but is it an exact equivalent?
That code works now (Rakudo 2018.04) and prints
$["en", "fr", "cz"]
as intended. It was probably a bug which was eventually solved.

Yii framework: Trying to get property of non-object

What I have:
public function beforeValidate() {
$offender = Accounts::model()->find(array('select'=>'id','condition'=>'username=:username','params'=>array(':username'=>$this->offender)));
$informer = Accounts::model()->find(array('select'=>'id','condition'=>'username=:username','params'=>array(':username'=>$this->informer)));
$this->offender = $offender->id;
$this->informer = $informer->id;
return parent::beforeValidate();
}
What I get:
PHP Notice, that says, that i'm trying to get property "id" of non-object $offender and $informer.
But those are 100% objects:
var_dump($offender):
object(Accounts)[46]
var_dump($informer):
object(Accounts)[46]
And it actually sets the right id, but shows that notice anyway. What is wrong?
SOLVED
Can't post it as official answer for six more hours, so i just leave it here:
Actually, the problem was in double beforeValidate() call.
AbuseController.php:
if(isset($_POST['AbuseReport']))
{
$model->attributes=$_POST['AbuseReport'];
if($model->validate())
{
$model->save();
}
}
First time it validates on $model->validate(), and replaces $this->offender and $this->informer with correct ID's. Second time it validates on $model->save();, but model returns null this time, because $this->offender is already ID, but it expects username.
The whole solution to this is to disable second validation: $model->save(false);.
use isset or is_object
if(isset($offender->id) || is_object($offender->id)){
$this->offender = $offender->id;
$this->informer = $informer->id;
}

Call to a member function num_rows() on a non-object [duplicate]

This question already has answers here:
Call to a member function on a non-object [duplicate]
(8 answers)
Closed 8 years ago.
I need to get the number of rows of a query (so I can paginate results).
As I'm learning codeigniter (and OO php) I wanted to try and chain a ->num_rows() to the query, but it doesn't work:
//this works:
$data['count'] = count($this->events->findEvents($data['date'], $data['keyword']));
//the following doesn't work and generates
// Fatal Error: Call to a member function num_rows() on a non-object
$data['count2'] = $this->events->findEvents($data['date'], $data['keyword'])->num_rows();
the model returns an array of objects, and I think this is the reason why I can't use a method on it.
function findEvents($date, $keyword, $limit = NULL, $offset = NULL)
{
$data = array();
$this->db->select('events.*, venues.*, events.venue AS venue_id');
$this->db->join('venues', 'events.venue = venues.id');
if ($date)
{
$this->db->where('date', $date);
}
if ($keyword)
{
$this->db->like('events.description', $keyword);
$this->db->or_like('venues.description', $keyword);
$this->db->or_like('band', $keyword);
$this->db->or_like('venues.venue', $keyword);
$this->db->or_like('genre', $keyword);
}
$this->db->order_by('date', 'DESC');
$this->db->order_by('events.priority', 'DESC');
$this->db->limit($limit, $offset); //for pagination purposes
$Q = $this->db->get('events');
if ($Q->num_rows() > 0)
{
foreach ($Q->result() as $row)
{
$data[] = $row;
}
}
$Q->free_result();
return $data;
}
Is there anything that i can do to be able to use it? EG, instead of $data[] = $row; I should use another (OO) syntax?
You function findEvents is returning $data which you declared to be an array at the start. This is not an object and does not allow you to access functions using the member access syntax.
To count the number of values in an array see count
Also, from what I understand you not only want the query results returned as an array, but you want to be able to access methods on the result $Q = $this->db->get('events'); However this is not possible as this is a local variable and it is not being returned out. The function here has a result type of array and is not an object and thus has no access to anything, but the array. One solution to this is to return an associative array:
return array("results" => $data, "count" => $Q->num_rows());
Then use array syntax to access the count or the results. Another option is return a new object that has result and count fields and use accessor methods to get to those.