[PEAK] model unit tests and a non-association QueryLink

Phillip J. Eby pje at telecommunity.com
Tue Nov 9 21:02:41 EST 2004

At 07:00 PM 11/9/04 -0500, R. David Murray wrote:
>My unit testing is going pretty well.  I built a test harness for
>my model tests that worked fine for most of the classes.  Now,
>however, I'm trying to deal with a field that in my real storage
>implementation is a QueryLink, but is not an association in the
>Here's what I've tried:
>class Harness(binding.Component):
>     class dummyDM(storage.EntityDM):
>         def _new(self, ob):
>             if not hasattr(ob, id):
>                 self.lastid = getattr(self, 'lastid', 0) + 1
>                 ob.id = str(self.lastid)
>             self._save(ob)
>             return ob.id
>         def _save(self, ob):
>             self.data[ob.id] = ob
>         def _load(self, id, ob):
>             return self.data[id].__dict__
>     class unbilledTransactionsForBilledAccount(storage.QueryDM):
>         Transactions = binding.Obtain(storage.DMFor(Transaction))
>         def _load(self, oid, ob):
>             print "unbilled called"
>             return [ x for x in Transactions.data if x.billedaccount.id 
> == oid
>                 and not x.billedon ]
>     class AccountDM(dummyDM):
>         unbilled = binding.Obtain(PropertyName('test.unbilled'))
>         def _new(self, ob):
>             ob.unbilledtransactions = storage.QueryLink(self.unbilled[ob.id])
>             super(Harness.AccountDM,self)._new(ob)
>     Account = binding.Make(AccountDM, offerAs=[storage.DMFor(Account)])
>     unbilled = binding.Make(unbilledTransactionsForBilledAccount,
>         offerAs=[PropertyName('test.unbilled')])
>     for m in (g for g in models if g!='Account'):
>         exec("%s = binding.Make(dummyDM, offerAs=[storage.DMFor(%s)])" % 
> (m, m))
>However, when I run my tests that print statement in unbilled never gets run,
>and the unbilledtransactions attribute always has the value [].
>I'm sure it is something simple I'm overlooking (like maybe I can't set
>an attribute to a QueryLink directly?)

That's correct.  A QueryLink is an implementation thing.  Remember that 
'_load()' and '_new()' deal with the object's implementation; they should 
not be using the client API.  Setting an attribute is client API.

>, and there may well be easier
>ways to do this or I may be doing other things wrong in this harness.
>So any comments are welcome.

Account._load() should put the querylink in the state dictionary that it 
returns; it's not conceptually part of _new.

>Hmm.  Maybe my storage model is flawed?  I mean, how can a QueryLink
>know that it needs to invaliate its value when the database rows
>it is based on change, when it isn't being updated by an association?


>In case it helps, the model is that a transaction has a 'billedon'
>of None until it is added to a bill, at which time its billedon
>becomes the bill it was assigned to.  So an account's unbilledtransactions
>are all those transactions for which it is the billed account but
>where the transaction hasn't yet been assigned to a bill.

So there should be a relationship from Account.transactions <-> 
Transaction.billedaccount; each should have a 'referencedEnd' pointing to 
the other.

At that point, 'unbilledtransactions' could then be a derived attribute.

More information about the PEAK mailing list