Page 1 of 1
Mocking Fields
Posted: Sun Oct 16, 2005 11:09 am
by Ambush Commander
I'm currently using ADOdb as my database wrapper library. Its recommended method of reading rows involves the method MoveNext() and the property $Fields. Because fields is a property, not a method, I can't seem to mock it using SimpleTest. What do I do?
I've determined two possible solutions: I can have MoveNext() set the property for me (don't know how I'd go about doing that), or I can have the class be aware when it's working with a mock and then call getMockFields() if $Fields is not set...
Posted: Sun Oct 16, 2005 7:09 pm
by sweatje
Can't you just set it to what you want it to return?
Code: Select all
$rs =& new MockAdodbResultset($this); //or whatever driver, etc.
$rs->fields = $something;
Posted: Sun Oct 16, 2005 7:11 pm
by Ambush Commander
It has this iterative property, that is, when you call MoveNext(), its value changes.
Hmm... I just may end up hand coding the mock myself...
Posted: Mon Oct 17, 2005 4:37 pm
by McGruff
I haven't used adodb: are you really supposed to access the $field property directly, without a getter?
Posted: Mon Oct 17, 2005 7:47 pm
by Ambush Commander
Yeah (according to the docs anyway). I heard making fields public is the "cardinal sin" of oop programming.
Posted: Mon Oct 17, 2005 8:36 pm
by McGruff
I think I would add a new getter to the class. Testing problems can often be a smell which highlights a design issue.
I have to say Adodb doesn't really click with me - maybe I just don't get its subtleties.
Posted: Mon Oct 17, 2005 8:53 pm
by sweatje
I use ADOdb all the time, including in Unit Tests, and I did not even remember the $fields attribute. Isn't that only for trying to extract metadata about the resultset? Can you describe more of what you are trying to accomplish. Perhaps there is another tact to take that is more testable.
Posted: Mon Oct 17, 2005 9:13 pm
by Ambush Commander
Well, currently, I'm using getArray, which returns the entire result set. Here's some usage code via fields:
Code: Select all
while (!$recordSet->EOF) {
print $recordSet->fields[0].' '.$recordSet->fields[1].'<BR>';
$recordSet->MoveNext();
}
Documentation here:
http://phplens.com/lens/adodb/docs-adodb.htm
Implementation of ADOdb makes it costly to transparently use a subclass (you have to do some reflective programming to make it work). Moving result set creation into its own method will make a partial mock required. I suppose there "should" be some method somewhere out there for grabbing the contents of Field... though checking the source didn't seem to have anything. Of course, editing the actual library file is out of question...
Posted: Mon Oct 17, 2005 9:23 pm
by sweatje
I know it is in the docs, my question is more why are you using it? I use getArray() all the time and that is usually more than sufficient for my needs.
I would much rather do:
Code: Select all
foreach ($rs->getArray() as $row) {
echo $row['field_one'], ' ', $row['field_two'], '<br>';
}
Saves a line and looks cleaner to me.
Posted: Mon Oct 17, 2005 9:32 pm
by Ambush Commander
Because it makes things a bit harder to factor out. Consider:
Code: Select all
function &_load($rs) {
$array = $rs->GetArray();
if (empty($array)) return null;
$id = (int) $array[0][$this->_primaryKey];
$result =& $this->getObject($id);
if ($result != null) return $result;
$result =& $this->_doLoad($id, $array[0]);
$this->setObject($id, &$result);
$this->setObjectByUnique(&$result);
return $result;
}
//should be reimplemented to recycle the _load function, but that
//won't work until we manage to mock $rs->Field, a field.
function &_loadAll($rs) {
$array = $rs->GetArray();
$resultarray = array();
foreach($array as $value) {
$id = (int) $value[$this->_primaryKey];
$result =& $this->getObject($id);
if ($result != null) {
$resultarray[] =& $result;
continue;
}
$result =& $this->_doLoad($id, $value);
$resultarray[] =& $result;
$this->setObject($id, &$result);
$this->setObjectByUnique(&$result);
}
return $resultarray;
}
See the duplication.
Ideally (and according to PoEAA), the loadAll function piggybacks off the load function in a manner like:
Code: Select all
function &_loadAll($rs) {
$result = array();
while($rs->next()) {
$result[] =& $this->_load($rs);
}
return $result;
}
Posted: Mon Oct 17, 2005 10:54 pm
by sweatje
shouldn't something like this work?
Code: Select all
function &$_loadFromRow($row) {
if (empty($row)) return null;
$id = (int) $row[$this->_primaryKey];
$result =& $this->getObject($id);
if ($result != null) return $result;
$result =& $this->_doLoad($id, $row[0]);
$this->setObject($id, &$result);
$this->setObjectByUnique(&$result);
return $result;
}
function &_load($rs) {
$array = $rs->GetArray();
if (empty($array)) return null;
return $this->_loadFromRow($array[0]);
}
function &_loadAll($rs) {
$resultarray = array();
foreach($rs->GetArray() as $row) {
$resultarray[] =& $this->_loadFromRow($row);
}
return $resultarray;
}
Posted: Tue Oct 18, 2005 2:37 pm
by Ambush Commander
It should work. ADOdb discourages using getArray for performance reasons, but I guess it will suffice for now.
Posted: Tue Oct 18, 2005 2:46 pm
by sweatje
I think this could be shorter as well (obviously not tested

)
Code: Select all
function &_load($rs) {
return $this->_loadFromRow($rs->getRow());
}
Posted: Tue Oct 18, 2005 2:54 pm
by Ambush Commander
Ah, nevermind, I found the function I was looking for: FetchRow()