6

After reading the last book from Robert C. Martin, I've tried a to develop some big Go applications following clean architecture. While writing interactors, I end up with a lot of complex unit tests, because the interactor has a lot of external dependencies.

What are the best practices for testing the interactors? Should I test the happy path only using an integration test? Using unit tests in the interactor I end up with a lot of mocks, it's not something I'm happy about.

Any advice or comments on this?


One of the applications on which I'm working is this one: https://github.com/luistm/banksaurus . It's far from great, but it's mostly a playing ground.


The first conclusion I get from this question is that one must understand the difference between Mock, Fake and Stub.

luistm
  • 163
  • 5
  • What do you mean by "complex" unit test. What do you mean "lot of " dependencies? Did you try using fakes instead of mocks? – Euphoric Apr 02 '18 at 05:47
  • Would you mind to copy here an example of one of these "bloated" interactors and its unit test? No need to copy all the classes involved. For the sake of the question, avoid to use meaningless names like Foo, Bar, X, A. You can leave the class, methods and var names as they are. – Laiv Apr 02 '18 at 07:40
  • You can see an example here: https://github.com/luistm/banksaurus/blob/master/bank/transactions/interactors_test.go . I really don't like the way this is done. – luistm Apr 02 '18 at 14:11
  • 1
    Doesn't go's testing framework have something similar to [NUnit's TestCase](http://nunit.org/docs/2.6/testCase.html)? Because it seems you are replicating that functionality inside your tests. Another thing I recommend trying is faking, not mocking, the dependencies. My experience is that faking produces much simpler test code than mocking. – Euphoric Apr 02 '18 at 15:11
  • What Euphoric is saying, correct me if I'm wrong, is: [use stubs instead of mocks](https://stackoverflow.com/q/3459287/5934037) – Laiv Apr 02 '18 at 17:55
  • I have to read those suggestions. Thank you to everyone for commenting. – luistm Apr 02 '18 at 19:37
  • It is unlikely the average Go developer would promote the complexity added by Martin's recommendations. Go was developed to reduce complexity. Cargo cult design techniques, such as SOLID, are designed to increase complexity. – Frank Hileman Apr 02 '18 at 21:25
  • @FrankHileman Then why Go developers use language at all? Shouldn't they be using machine code? /s – Euphoric Apr 03 '18 at 06:38
  • @Euphoric If you think machine code is less complex, go for it. Higher level code does not require the type of baggage recommended by Martin. – Frank Hileman Apr 03 '18 at 20:25

1 Answers1

4

While writing interactors, I end up with a lot of complex unit tests, because the interactor has a lot of external dependencies.

Really?

enter image description here

The controller passes you a request model, you build a response (after talking to some entities) and send that to a presenter. That shouldn't be a complex unit test because any one interactor shouldn't be doing anything more complex than that. I don't see any dependencies here that must be external.

You shouldn't only test the happy path. 80% of coding is dealing with things gracefully when they go wrong. But stick to fixing problems that can actually be caused.

Using unit tests in the interactor I end up with a lot of mocks.

Man I wish I knew exactly what you meant when you say mocks but whatever kind of test double you use, don't use it to test things no one cares about. You could make all sorts of crazy tests by making your own test entities with asserts in them I'm sure but what we care about here is sending the right response model to the right presenter. If you're testing more than that I don't know why.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • But for instance, should I test that I can deal with every error? Should I test that when you pass data to the presenter, it can return an error? Looks like a lot of tests to do in the interactor. Maybe I should unit-test every function and just write some happy path integration test to the interactor. That's my feeling right now. – luistm Apr 03 '18 at 07:19
  • So, regarding "no external dependencies", look at Uncle Bob's example of a Use Case ("Interactor" in his lingo) at https://youtu.be/Nsjsiz2A9mg?t=767. Step # 2 "validates all data" might include calling a remote Zip Code verification web service to verify the shipment destination address, and calling another remote service to verify the credit card info on the payment info. Step # 3 "creates order" means hitting the database to write all this data for the 5 areas of input, and generate the order ID. How does that not involve external dependencies? – Basil Bourque Aug 22 '22 at 06:57