reflex-dev / reflex

🕸️ Web apps in pure Python 🐍
https://reflex.dev
Apache License 2.0
20.46k stars 1.18k forks source link

Testing `rx.ComponentState` by direct initialization broken in `v0.6.2+` (SetUndefinedStateVarError) #4343

Closed TimChild closed 1 week ago

TimChild commented 2 weeks ago

Describe the bug Before v0.6.2, it was possible to directly initialize ComponentState for testing purposes (same as directly initializing State instances), but since v0.6.2 I get an error:

reflex.utils.exceptions.SetUndefinedStateVarError: The state variable 'some_var' has not been defined in 'DummyComponent'. All state variables must be declared before they can be set.

I've not been able to really narrow down where the issue comes from, but below is a very minimal example that demonstrates the issue.

To Reproduce Steps to reproduce the behavior:

import reflex as rx

class DummyComponent(rx.ComponentState):
    some_var: int = 0

    def do_something(self):
        self.some_var += 1

def test_direct_component_init():
    state_inst = DummyComponent()

    assert state_inst.some_var == 0

    state_inst.do_something()

    assert state_inst.some_var == 1

Running pytest on this with reflex==0.6.1 passes... With reflex==0.6.2 (or higher) it fails.

Expected behavior Should be able to directly initialize ComponentState instances for testing purposes.

Specifics (please complete the following information):

Is there a different recommended way to test basic behaviour of ComponetState subclasses? (and is this still the right approach to take with rx.State subclasses if not for rx.ComponentState subclasses?).

Thanks!

linear[bot] commented 2 weeks ago

ENG-4086 Testing `rx.ComponentState` by direct initialization broken in `v0.6.2+` (SetUndefinedStateVarError)

benedikt-bartscher commented 2 weeks ago

@TimChild i suggest adjusting the test to smth like this:

from typing import override

import reflex as rx

class DummyComponent(rx.ComponentState):
    some_var: int = 0

    def do_something(self):
        self.some_var += 1

    @override
    @classmethod
    def get_component(cls):
        return rx.box()

def test_direct_component_init():
    state_cls = DummyComponent.create().State
    assert state_cls is not None
    state_inst = state_cls()
    assert isinstance(state_inst, DummyComponent)

    assert state_inst.some_var == 0

    state_inst.do_something()

    assert state_inst.some_var == 1

@reflex-dev maybe we should raise an exception if someone tries to directly instantiate a ComponentState?

TimChild commented 2 weeks ago

@benedikt-bartscher , oh yes, that make sense now that I see it, thanks!

It would definitely be nice to get a helpful error message there, or for it to be possible to directly instantiate if in a testing environment, but your solution does make sense.

benedikt-bartscher commented 2 weeks ago

@TimChild I implemented better error messages in #4347