cfpb / django-flags

Feature flags for Django projects
https://cfpb.github.io/django-flags/
Creative Commons Zero v1.0 Universal
256 stars 31 forks source link

How to use flags in tests? #121

Closed dimaqq closed 4 months ago

dimaqq commented 9 months ago

For example, I'd like to test 2 code paths, with flag on and off. In my case the flag is only sourced from the database (not url params).

I imagine this is a common question, if there's a good answer already, I volunteer to add it to documentation.

willbarton commented 9 months ago

@dimaqq Sure! Because FLAGS are definable in Django settings, you can use Django's standard override_settings to test with a flag off and on.

An example is in Django-Flags's own tests, where we test flagging URLs:

class FlaggedCodeTestCase(TestCase):
    @override_settings(FLAGS={"MY_FLAG": [("boolean", True)]})
    def test_flag_enabled(self):
        # Do the thing that requires the flag to be enabled

    @override_settings(FLAGS={"MY_FLAG": [("boolean", False)]})
    def test_flag_disabled(self):
        # Do the thing that requires the flag to be disabled

Here one tests overrides FLAGS in Django settings to set it with a boolean condition that is always True, to test the flag-enabled code path, and the other tests with it set to a boolean condition that is always False to test the code path when the flag is not enabled.

Unless you're overriding FLAG_SOURCES in settings to remove flags.sources.SettingsFlagsSource, this will work and will be database independent.

If for some reason you've completely disabled the flags.sources.SettingsFlagSource and are only allow database flags, you can either use override_settings to change FLAG_SOURCES for the tests, or if you really want to use the database, you can create aFlagStateobject along the same lines with a boolean condition that isTrueto test the flag-enabled code path, and then create one with a boolean condition that isFalse` to test the not-enabled path.

We don't have a direct example of this in Django-Flags's tests (because it's more complex, and is database-dependent), but the FlagState model tests show how you can do this. It could look something like this:

class FlaggedCodeTestCase(TestCase):
    def test_flag_enabled(self):
        FlagState.objects.create(
            name="MY_FLAG", condition="boolean", value="True"
        )
        # Do the thing that requires the flag to be enabled

    def test_flag_disabled(self):
        FlagState.objects.create(
            name="MY_FLAG", condition="boolean", value="True"
        )
        # Do the thing that requires the flag to be disabled

I hope this helps! Generally, there's nothing inherently unique about testing with flags enabled or disabled in Django-Flags, it uses pretty standard Django testing paradigms with either settings or models.