Kolos65 / Mockable

A Swift macro driven auto-mocking library.
MIT License
199 stars 14 forks source link

Add strict mode #25

Closed xmanu closed 3 months ago

xmanu commented 3 months ago

This PR implements strict mode and lenient mode as outlined in #15.

It adds an additional constructor to the generated mock classes that allows for (optionally) switching to strict mode.

let mockService = MockService(strict: true)

When in strict mode the following now is in effect:

  1. Methods returning void now have to be mocked
  2. Properties returning an optional type have to be mocked (this is the same as it is right now)

In lenient mode (without using the new constructor or by passing false), the following behavior is in effect:

  1. Methods returning void do not have to be mocked and will always run (the current behavior)
  2. Properties returning an optional will automatically return nil

Even though the lenient mode is currently implemented as default, this could be changed (I would love to have strict mode as default). I still chose to go for lenient as the default, as this is a breaking change that is less likely to be problematic for existing users.

I hope this can be merged and introduced in a new version. I suggest to warn users in the changelog about the differing behavior for optional properties returning nil by default.

I have updated the README to include an explaining section about strict mode.

Kolos65 commented 3 months ago

Thanks for the contribution @xmanu 🙏

I need some time to think about this feature as it might be a better idea to implement this as part of a bigger update, where we add something like relaxed mode in mockk.

xmanu commented 3 months ago

Sure! Please let me know if you have other ideas how to incorporate this. I would love to see a bigger update that clarifies how Mockable handles strict and relaxed mode. Right now it is "between both worlds". We are replacing our own mocking framework with Mockable and we would appreciate the strict (and more similar to mockk) behavior in our tests.

I see that you updated the implementation of Mocker in a recent commit. I am not very happy about the code duplication of the mock method I did in this PR, to allow the full relaxed mode. There probably is a better solution with more knowledge about the full Mockable implementation. Please get back to me if I should update this PR, or if you want to work on a solution yourself.

Kolos65 commented 3 months ago

What do you mean it is "between both worlds"?

As I see, the current implementation is the same as strict mode = true.

Kolos65 commented 3 months ago

I would rather not require void functions to be mocked explicitly (even in strict mode) as they do not have a return value and it is semantically weird.

Does having this in strict mode have any advantages?

xmanu commented 3 months ago

No. This is unfortunately not the case. Strict mode (my PR and also others like mockks implementation) will require void functions to be mocked. This is not the case for Mockable. In this aspect Mockable is relaxed.

On the other hand properties returning optionals are required to be mocked. This is strict behavior. Therefore it is between both worlds.

xmanu commented 3 months ago

The advantage of requiring mocks for void returning functions, is that it requires the implementor of tests to be very specific. It is easy to miss specifying that a method should be called and to verify that. By requiring mocking the result explicitly you get two guarantees:

  1. No unexpected methods on your mocks were called while testing.
  2. The reader of the tests can easily verify that the mocked methods make sense and these should be called.
Kolos65 commented 3 months ago

I see... Valid points!

I think the best would be to change the implementation to be fully stric mode by default and implement relaxed mode later with support for optionals, voids and other values expressible by literals.

I will prepare this today and submit a new PR!

Thanks for the contribution!

xmanu commented 3 months ago

That sounds great! Thank you a lot!