pschanely / CrossHair

An analysis tool for Python that blurs the line between testing and type systems.
Other
1.03k stars 49 forks source link

Support correct and compilable representation of enum values in the cover/pytest mode #216

Closed azewiusz closed 1 year ago

azewiusz commented 1 year ago

Is your feature request related to a problem? Please describe. The problem is when I want to represent a system under test as a model and help myself by using signatures by representing state names using enums. Generally information that comes from crosshair-tool is readable with one exception - pytest cover default generated representations of enum values.

@dataclasses.dataclass
class CustomEnumZero(Enum):
    ENUM_VALUE1 = 1
    ENUM_VALUE2 = 2
    ENUM_VALUE3 = 3

class CustomEnumOne(Enum):
    ENUM_VALUE1 = 1
    ENUM_VALUE2 = 2
    ENUM_VALUE3 = 3

class CustomEnumWithRepr(Enum):
    ENUM_VALUE1 = 1
    ENUM_VALUE2 = 2
    ENUM_VALUE3 = 3

    def __repr__(self):
        return str(self)

And then we have system under test consisting of these functions:

def example_logic_with_enum_with_dataclass(myenum: Optional[CustomEnumZero]):
    if myenum == CustomEnumZero.ENUM_VALUE1:
        return myenum

def example_logic_with_enum_without_custom_repr(myenum: Optional[CustomEnumOne]):
    if myenum == CustomEnumOne.ENUM_VALUE1:
        return myenum

def example_logic_with_enum_with_repr(myenum: Optional[CustomEnumWithRepr]):
    if myenum == CustomEnumWithRepr.ENUM_VALUE1:
        return myenum

We run cover command on them and get the following results

def test_example_logic_with_enum_with_dataclass_2():
    assert example_logic_with_enum_with_dataclass(CustomEnumZero()) == CustomEnumZero()

def test_example_logic_with_enum_without_custom_repr_2():
    assert example_logic_with_enum_without_custom_repr( < CustomEnumOne.ENUM_VALUE1: 1 >) == < CustomEnumOne.ENUM_VALUE1: 1 >

def test_example_logic_with_enum_with_repr_2():
    assert example_logic_with_enum_with_repr(CustomEnumWithRepr.ENUM_VALUE1) == CustomEnumWithRepr.ENUM_VALUE1

As we can see first test from top - uselsess Second one is not interpretable / compilable (but still human readable) The last one is correct for pytest.

Describe the solution you'd like

It would be nice if crosshair tool was generating compilable tests out of the box or was at least giving hints that generated code requires enums to be declared in a sepcific way like with example CustomEnumWithRepr

Describe alternatives you've considered I have no concrete idea how to solve this by built-in procedure that will automatically process enums correctly for purpose of cover functionality.

Example repo with above code: https://github.com/azewiusz/for-crosshair-tool/tree/main/example1

pschanely commented 1 year ago

I fully agree with your suggestion. I think I should be able to work on a fix this week!

pschanely commented 1 year ago

I have a potential fix in v0.0.43. Would you give it a try and let me know if it helps?

One additional thing I noticed: CrossHair does not try very hard to get the imports right, and enum references fall into the category of things it won't produce. Opened #220 for this issue, though I'm not sure when I'll be able to get to it.

azewiusz commented 1 year ago

Thanks, I will try it out maybe during the coming weekend. With imports I knew that and so far for my needs I was doing manual importing to run with coverage.

pt., 27 paź 2023 o 15:22 Phillip Schanely @.***> napisał(a):

Closed #216 https://github.com/pschanely/CrossHair/issues/216 as completed.

— Reply to this email directly, view it on GitHub https://github.com/pschanely/CrossHair/issues/216#event-10791752657, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABDDVS36AVYBB4XRBLBIX7LYBOYS5AVCNFSM6AAAAAA6JLZPTSVHI2DSMVQWIX3LMV45UABCJFZXG5LFIV3GK3TUJZXXI2LGNFRWC5DJN5XDWMJQG44TCNZVGI3DKNY . You are receiving this because you authored the thread.Message ID: @.***>

pschanely commented 1 year ago

Turns out that my earlier fix only helped for arguments to the function under test, not to the result we compare to the return value. Working on this presently.

pschanely commented 1 year ago

Hopefully closed the loop on this in v0.0.44. Please do let me know if you find any other cases!

pschanely commented 1 year ago

One last issue when the enum is nested:

File "crosshair\path_cover.py", line 171, in import_statements_for_references
class_name, _ = ref.qualname.split(".", 2)
^^^^^^^^^^^^^
pschanely commented 1 year ago

Fixed in v0.0.45