[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
>model.
>
>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?
Correct.
>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