0

I'm looking for a way to implement a custom parametrisation logic for my tests. My code tends to use a polymorphic design and therefore it feels like the most sensible way to test all the polymorphic methods in a single test. A mock example of how I'd decorate the test currently:

@mark.parametrize(
    ("cls", "input", "expected"),
    [
        (ClsA, 0, True),
        (ClsA, 1, False),
        (ClsB, 0, False),
        (ClsB, 1, True),
)

Within the test, I'd call the method on each class instance after instantiating with input and expect expected. It feels as though (especially with larger sets of parameterisations) that it would make more sense to "set" ClsA once for all parameterisations that use it, ClsB once for all parameterisations that use it etc. If you only had one cls arg, you could do that with:

@mark.parameterize(
    "cls",
    [ClsA],
)
@mark.parameterize(
    ("input", "output"),
    [
         (0, True),
         (1, False),
    ]
)

However you can't use this syntax with multiple cls arguments as it would create the product of the different decorators, lacking a way to assign certain parameters in one decorator to only be used with certain parameters in another. Is there a way to do what I'm trying to achieve? Otherwise, I have to write out the same argument repetitively which feels WET.

Alesi Rowland
  • 379
  • 2
  • 16
  • It would help to give an example for what this approach does not work, as it works for your given example. – MrBean Bremen Nov 11 '20 at 12:57
  • 1
    Take a look at this [related question](https://stackoverflow.com/q/4923836). From your examples it's hard to tell if this is a good approach in the first place. Does `assert ClsA(0).method() == True` really test the same thing as `assert ClsB(0).method() == False`? Also keep in mind that you restrict your production code to be compatible with the test by doing this. What if you eventually implement `ClsC` which takes two parameters in its initializer? IMO, it's more important for tests to be concise and expressive rather than DRY. – danzel Nov 11 '20 at 13:39
  • @MrBeanBremen - The second example is an approach which doesn't work. If you try and expand the logic to use a second cls, you can't use that approach. – Alesi Rowland Nov 11 '20 at 14:13
  • @danzel - Thanks for the advice in terms of good design - always appreciate this! I think the question you linked is the closest to what I want so I'm considering my question answered. – Alesi Rowland Nov 11 '20 at 14:15
  • @danzel - I appreciate the point about different initialisation signature, however with these classes they are used such that their signatures must match (i.e. they are used interchangeably in the code) - that is, I've sacrificed this option so that I can build simpler code in other places. – Alesi Rowland Nov 11 '20 at 14:34
  • @AlesiRowland - works fine for me, just tried it - can you show what get if you do that? – MrBean Bremen Nov 11 '20 at 15:28
  • @MrBeanBremen @mark.parametrize("cls". [ClsA, ClsB])@mark.parameterize(("input", "output"), [(0, True), (1, False), (0, False), (1, True)] would be how I'd expand it using the above syntax. This will run 8 tests, 4 of which are unintentional. – Alesi Rowland Nov 11 '20 at 16:43
  • 1
    Ah, sorry, I misread your example - right, in this case I agree with @danzel. – MrBean Bremen Nov 11 '20 at 17:17
  • No worries! Its a bit of a niche question anyway – Alesi Rowland Nov 12 '20 at 12:00

0 Answers0