Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ ChangeLog
------------------

- Add support for Django 5.2
- :issue:`1119`: Apply a field's :class:`~factory.Transformer` to caller-provided values
while a :class:`~factory.Trait` overriding that field is not enabled


3.3.3 (2025-02-03)
Expand Down
8 changes: 7 additions & 1 deletion factory/declarations.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,9 @@ def evaluate_post(self, instance, step, overrides):
def evaluate_pre(self, instance, step, overrides):
choice = self.decider.evaluate_pre(instance=instance, step=step, overrides={})
target = self.yes if choice else self.no
if choice and self.CAPTURE_OVERRIDES and '' in overrides:
# A call-time value (captured for `no_declaration`) replaces `yes_declaration`.
target = overrides.pop('')
# The value can't be POST_INSTANTIATION, checked in __init__;
# evaluate it as `evaluate_pre`
return self._unwrap_evaluate_pre(
Expand Down Expand Up @@ -603,7 +606,7 @@ def __init__(self, **overrides):
def as_declarations(self, field_name, declarations):
overrides = {}
for maybe_field, new_value in self.overrides.items():
overrides[maybe_field] = Maybe(
maybe = Maybe(
decider=SelfAttribute(
'%s.%s' % (
'.' * maybe_field.count(enums.SPLITTER),
Expand All @@ -614,6 +617,9 @@ def as_declarations(self, field_name, declarations):
yes_declaration=new_value,
no_declaration=declarations.get(maybe_field, SKIP),
)
# An inactive trait must not hide a base declaration capturing call-time values (e.g. a Transformer).
maybe.CAPTURE_OVERRIDES = getattr(maybe.no, 'CAPTURE_OVERRIDES', False)
overrides[maybe_field] = maybe
return overrides

def get_revdeps(self, parameters):
Expand Down
46 changes: 46 additions & 0 deletions tests/test_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ def test_override(self):
instance = WithMaybeFactory(one=True, two="NI")
self.assertIs(instance.one, True)
self.assertEqual(instance.two, "NI")

def test_override_forwarded_to_branch(self):
"""An explicit Maybe forwards a `field__` value to the chosen branch's transformer"""
instance = WithMaybeFactory(one=True, two__="ni")
self.assertEqual(instance.two, "NI")
self.assertIsNone(instance.three)


Expand Down Expand Up @@ -228,3 +233,44 @@ def test_trait_transform_applies_supplied(self):
self.assertEqual(instance.one, "ONE")
self.assertEqual(instance.two, "two")
self.assertIsNone(instance.three)

def test_inactive_trait_keeps_base_transform(self):
"""An inactive trait shouldn't prevent the base transformer from applying to caller-provided values"""
instance = WithTraitFactory(one="caller")
self.assertEqual(instance.one, "CALLER")

def test_inactive_trait_keeps_base_transform_force(self):
instance = WithTraitFactory(one=factory.Transformer.Force("caller"))
self.assertEqual(instance.one, "caller")

def test_active_trait_overridden_by_supplied(self):
"""An active trait's value should be overridden by caller-provided values"""
instance = WithTraitFactory(odds=True, one="caller")
self.assertEqual(instance.one, "caller")
self.assertEqual(instance.three, "three")


class WithTransformerTraitFactory(factory.Factory):
class Meta:
model = TestObject

class Params:
string = factory.Trait(one=factory.Transformer(234, transform=str))

one = factory.Transformer(123, transform=hex)


class TransformerTraitOverridesTransformerTest(TestCase):
"""A trait replacing a Transformer with another Transformer (#1119)."""

def test_trait_off(self):
self.assertEqual(WithTransformerTraitFactory().one, "0x7b")

def test_trait_off_applies_supplied(self):
self.assertEqual(WithTransformerTraitFactory(one=345).one, "0x159")

def test_trait_on(self):
self.assertEqual(WithTransformerTraitFactory(string=True).one, "234")

def test_trait_on_overridden_by_supplied(self):
self.assertEqual(WithTransformerTraitFactory(string=True, one=345).one, 345)