Open fwemaere opened 11 months ago
Yes, that would be nice and I already had this improvement in my head. But that's a low priority for us currently. Contributions are welcome.
The idea how to implement this would be quite simple: during code generation, if we have to create a fake of the sealed
class S
we could iterate through the subclasses of S
and if some of them are not base
or final
, we could make fake S
implement that subclass instead of S
.
Thought a bit more about it. I think that's totally fine and will be an actual improvement, to pass a fake of a child class as returnValue
argument of Mock.noSuchMethod
: it is only used while stubbing and that value is always thrown away.
I wouldn't pass the same fake value as returnValueForMissingStub
though, since that one is used for nice mocks, and picking one of the options will silently pick a code path that will always be executed. We likely don't want to make it uncontrollably.
Any work around for this yet?
provideDummy
/provideDummyBuilder
is a workaround. Eventually planned version 6 will allow mocking classes that have methods with sealed argument and/or return types (but not sealed classes themselves!) directly.
I don't have any timeline for version 6 though.
Thanks @yanok
@yanok can you maybe provide some more details on the decision process? I think it's a big decision to not support a newly added core feature of the Dart language.
@martin-formigas Well, sealed classes are mutually incompatible with mocks, so we don't have much choice.
Consider that you have
sealed class Pet {}
class Cat extends Pet {}
class Dog extends Pet {}
void speak(Pet pet) => switch (pet) {
Cat() => print('Meow'),
Dog() => print('Woof')
// Note that you don't need a "catch all" clause here, since Dart can see you exhausted all the possibilities.
}
Now imagine you managed to create a MockPet
somehow, which is a subtype of Pet
(but not a subtype of Cat
or Dog
). That means you can pass it to speak
function, but it can't handle MockPet
since there is no branch for it in switch
, so it would be a runtime error. Fortunately, the language doesn't allow you to create MockPet
, exactly for this reason.
You can mock Dog
or Cat
instead and this will work.
@martin-formigas so the decision process was actually on the language side: the language team wanted to get exhaustiveness for sealed classes (which is generally good), they did realize that it will break mocking and they decided exhaustiveness is more important.
We just have to follow here.
If you want my personal opinion, I'd do the same, but I wasn't involved in the process.
Got it, thanks for the insight! I think this issue can be closed then, since there will never be an actual resolution?
There are two separate issues with sealed classes:
provideDummy
, we could potentially do it better (see https://github.com/dart-lang/mockito/issues/675#issuecomment-1633870039) and eventually this issue should be solved by changing an API in the next version, see https://github.com/dart-lang/mockito/issues/684. I'd prefer to keep this bug open until then, so people could find it and see what the current state is, instead of filing new bugs.Hi @yanok I ran into one problem that if I have a StreamController
that have a sealed Event
how can I provideDummy for it
final events = StreamController<Event>();
when(mockService.events).thenAnswer((_) => events.stream);
provideDummy<Event>(/* an instance of Event */);
You might be able to drop the <Event>
part if an instance static type is Event
.
Thanks @yanok but that's not what I mean. I will inspect about this and get back to you later
I have some trouble when using sealed class.
This is a sample :
I have this error
`This means Mockito was not smart enough to generate a dummy value of type 'Pet'. Please consider using either 'provideDummy' or 'provideDummyBuilder' functions to give Mockito a proper dummy value.
Please note that due to implementation details Mockito sometimes needs users to provide dummy values for some types, even if they plan to explicitly stub all the called methods.`
I have to add
provideDummy(cat);
beforewhen
. If i do this it works.It would be nice if sealed class was compatible without provide dummy value !