Mocking Fields

Discussion of testing theory and practice, including methodologies (such as TDD, BDD, DDD, Agile, XP) and software - anything to do with testing goes here. (Formerly "The Testing Side of Development")

Moderator: General Moderators

Post Reply
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Mocking Fields

Post 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...
User avatar
sweatje
Forum Contributor
Posts: 277
Joined: Wed Jun 29, 2005 10:04 pm
Location: Iowa, USA

Post 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;
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post 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...
McGruff
DevNet Master
Posts: 2893
Joined: Thu Jan 30, 2003 8:26 pm
Location: Glasgow, Scotland

Post by McGruff »

I haven't used adodb: are you really supposed to access the $field property directly, without a getter?
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Yeah (according to the docs anyway). I heard making fields public is the "cardinal sin" of oop programming.
McGruff
DevNet Master
Posts: 2893
Joined: Thu Jan 30, 2003 8:26 pm
Location: Glasgow, Scotland

Post 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.
User avatar
sweatje
Forum Contributor
Posts: 277
Joined: Wed Jun 29, 2005 10:04 pm
Location: Iowa, USA

Post 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.
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post 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...
User avatar
sweatje
Forum Contributor
Posts: 277
Joined: Wed Jun 29, 2005 10:04 pm
Location: Iowa, USA

Post 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.
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post 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;
}
User avatar
sweatje
Forum Contributor
Posts: 277
Joined: Wed Jun 29, 2005 10:04 pm
Location: Iowa, USA

Post 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;
    }
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

It should work. ADOdb discourages using getArray for performance reasons, but I guess it will suffice for now.
User avatar
sweatje
Forum Contributor
Posts: 277
Joined: Wed Jun 29, 2005 10:04 pm
Location: Iowa, USA

Post by sweatje »

I think this could be shorter as well (obviously not tested ;) )

Code: Select all

function &_load($rs) {
        return $this->_loadFromRow($rs->getRow());
    }
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Ah, nevermind, I found the function I was looking for: FetchRow()
Post Reply