|
From: Marcus B. <ma...@la...> - 2003-11-24 00:44:30
|
Hi...
Jason Sweat wrote:
> Have to problems still getting at me. The first I solved by adding a method to
> the SimpleMock class. It may be that I still don't know how to use them, but
> here is the method and my reason for adding it:
>
> /**
> * Resets the Mock to an initial state
> * All returns and expectations are cleared
> * @public
> * @return void
> */
> function reset() {
> $this->_returns = array();
> $this->_return_sequence = array();
> $this->_expected_counts = array();
> $this->_max_counts = array();
> $this->_min_counts = array();
> $this->_expected_args = array();
> $this->_args_sequence = array();
> $this->clearHistory();
> }
>
> I am using the mock to simulate a database connection. There is some
> initialization that I have to go through when I create the object I am testing.
> I do not want to stub in these expectations and results at each step, so
> instead of always creating new objects to test, I decided to create a member of
> the test case and set it to an instance of the class I want to test. After
> that, I want to continue to use the same Mock object to run further tests, but
> it always wanted to remember the first tests. My hack was to add the reset()
> method. Not sure how you would feel about adding that to the real project.
I used to have a reset method on an earlier version, but removed it. The
reason is that the setUp() method in the unit tester kind of supercedes it.
I usually either add a method to the test case, such as &createMyMock(),
and call it from within the test method, or just add the creation in
the setUp() method to an object variable and reset the variable. There
are examples of both within the SimpleTest HTTP tests.
Creating the mock afresh for each test method is quite fast. By example
something like...
class DatabaseTest extends UnitTestCase {
...
function &createData() {
$iterator = &new MockDatabaseIterator($this);
$iterator->setReturnValueAt(0, array('name' => 'fred'));
$iterator->setReturnValueAt(1, array('name' => 'jim'));
$connection = &new MockDatabaseConnection($this);
$connection->setReturnReference('query', $iterator);
return $connection;
}
function testFirst() {
$db = &$this->createData();
$db->expectArguments('query', 'select * from people');
...
}
}
Is it possible to break the test method down so as to make it easier to
do this way? In the example below you know what will be called first.
Perhaps test the first bit, and then test the second execute call
assuming the first worked, in a separate test method.
I am afraid I don't want to add a reset() method having recently removed
it. In fact the clearHistory() method seems a bit pointless right now as
well. The reason for not using things like reset() is that tests could
accidently interfere and it would be an extra feature to have to add
when subclasing the mock objects. These mocks could have special
properties which make a mess of reset().
BTW, You don't need to hack the code to change the mock. You can just
inherit from it and use SimpleTestOptions::setMockBaseClass($class) to
tell SimpleTest to inherit from yours instead.
>
> My second problem may just be a pure PHP reference problem, but I can't seem to
> solve it. I want to test a database based DAO for my project. I had a factory
> method that is passing in the database connection object by reference to the
> constructor function for my db DAO object.
The $poConn?
> The DAO's constructs sets a member
> var _by reference_ to the connection object.
$_moConn I assume.
> In my test case, I then wanted to
> create the Mock connection. set it _by ref_ to a member var of the test case,
> and then pass the member var as the connection parameter to the DAO being
> tested.
>
> My though process was then that I could continue to manipulate the member var
> for the connection in the unit test case, and this would be the exact same
> object that was the connection for the DAO being tested.
It should be. There doesn't seem to be anything wrong.
> Somewhere along the
> way I am loosing the reference because the only way I can get things to work is
> to reference the private var for the connection of the object being tested.
This is kind of mixed up with the Reset() version of the mock as well so
I don't really understand what is happening. After the testInstanciate()
call there is a cache set up with a mock inside it, yes? I'll call the
actual objects CacheA and ConnA. We have the references...
$this->_moCache --> CacheA
$this->_moConn --> ConnA
CacheA --> ConnA
At the start of the second test method setUp() will be run again giving
this situation...
$this->_moCache --> CacheA
$this->_moConn --> ConnB
CacheA --> ConnA
$o_conn --> ConnA
$o_test --> CacheA
The interesting part is that because the setUp() is rerun before every
test method $this->_moConn and $o_conn no longer point at the same object.
I find it confusing to reuse the results of one test within another.
There is always the possibility of interference and even when it works
the dependent test could get refactored, breaking the other spuriously.
I would say have a method called createCachedDao() that sets up the
cache and call it at the start of both tests. That way everything is set
up from scratch. Here is a possible version...
class DbTest extends UnitTestCase {
function DbTest() {
$this->UnitTestCase();
}
function &createConnection() {
$o_rs_appl =& new MockADORecordSet($this);
$o_rs_appl->SetReturnValueAt(0, 'FetchRow', array('get_appl_id' =>
TEST_DAOPGDB_APPL_ID));
$connection =& new MockADOConnection($this);
$connection->SetReturnReferenceAt(0, 'Execute', $o_rs_appl);
$connection->ExpectArgumentsAt(
0,
'Execute',
array(
DATACACHE_DAOPGDB_SEL_APPL_ID,
array(TEST_APPLICATION)));
return $connection;
}
function &createLoadedCache(&$dao) {
return new DataCacheDaoPgDb(TEST_APPLICATION, $dao);
}
function TestInstanciate() {
$connection = &$this->createConnection();
$connection->expectCallCount('Execute', 1);
$cache =& $this->createLoadedCache($connection);
$this->AssertNoErrors();
$connection->Tally();
}
function TestEmptyCache() {
$result =& new MockADORecordSet($this);
$result->SetReturnValueAt(0, 'FetchRow', array('is_cached' => '0'));
$connection = &$this->createDao();
$connection->SetReturnReferenceAt(1, 'Execute', $result);
$connection->ExpectCallCount('Execute', 2);
$connection->ExpectArgumentsAt(
1,
'Execute',
array(
DATACACHE_DAOPGDB_CHK_DATA,
array(TEST_DAOPGDB_APPL_ID, TEST_DAOPGDB_BLANK)));
$cache =& $this->createLoadedCache($connection);
$id = TEST_DAOPGDB_BLANK;
$this->AssertFalse($cache->IsCached($id));
$connection->Tally();
}
}
> Thanks.
I only hope that helps. Bounce it back if it doesn't.
A lot of people are also having trouble with the ordering of the test
methods. If I have these methods in a test case...
class MyTest extends UnitTestCase {
...
setUp() { ... }
tearDown() { ... }
someMethod() { ... }
testOne() { ... }
testTwo() { ... }
testThree() { ... }
}
The call order is setUp, testOne, tearDown, setUp, testTwo, tearDown,
setUp, testThree, tearDown. someMethod() will only be called if another
method invoked it.
yours, Marcus
--
Marcus Baker, ma...@la..., no...@ap...
|