<div dir="ltr">I have very opinionated views around this topic. Hopefully you'll find them interesting.<div><br></div><div>I think of a service as a broker between two very different parts of your application, the web bit, and the business bit ( for want of better terms ). The sole purpose of a service is to provide a convenient method for the web bit to delegate work to the business bit. It does this by providing</div><div><br></div><div>1) A consistent method for making a call, which takes a parameter that contains all the relevant information collected by the web bit (e.g. a params hash, or perhaps an assembled model object)</div><div>2) A consistent method for receiving the result of the call which includes; a result boolean, and perhaps a modified thing that was sent.</div><div><br></div><div>This interface is somewhat clunky by nature so some strict rules should be applied.</div><div><br></div><div>1) The service should follow a naming convention, i.e. have Service in its class name.</div><div>2) A service should NEVER call another service.</div><div>3) Services should not do work, instead they should ask for work to be done (generally by PORO's)</div><div>4) Services should follow a strict contract where all services return the same thing (I call this a ServiceResult)</div><div><br></div><div>When you follow this pattern you get some major benefits</div><div><br></div><div>a) No need to ever mock a service</div><div>b) You can call services directly from integration tests to allow integration tests to bypass the web layer when setting things up</div><div>c) You get to pure OO, or perhaps functional if that's your thing quickly and cleanly. Once you are there you are working with things that are much easier to Unit test. </div><div>d) You can use the service layer as a testing cut off point. Below services you can Unit test. Anything that involves a service by definition is an integration test.</div><div><br></div><div>This pattern works very well with BDD with each piece of behaviour ending up having its own service.<br></div><div><br></div><div>I've dealt with a lot of difficult code where</div><div><br></div><div>i) Almost everything ends up being a Service (to the point where PORO's were not being used at all)</div><div>ii) You have Service spaghetti were you have complex Service chains, and a service has no idea whether its an endpoint or midpoint, and has to cater for both uses. Inevitably such services become more complex than they should because they have too many responsibilities.</div><div><br></div><div>For me a Service calling another Service is a major anti-pattern, and in codebases were I have control it is not allowed.</div><div><br></div><div>Anyhow I hope you find the above interesting. If you do end up following this pattern you will find your test problems around services will just go away.</div><div><br></div><div>All best :)</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 6 Jun 2024 at 11:59, Patrick Gleeson <<a href="mailto:patrick.c.gleeson@gmail.com">patrick.c.gleeson@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr">Hi LRUG,<div><br></div><div>I've been bitten by bad unit tests in the past (mostly ones I've written myself), and lean towards integration tests (a la <a href="https://x.com/rauchg/status/807626710350839808" target="_blank">https://x.com/rauchg/status/807626710350839808</a>) when given the choice. But I want to believe good unit tests are achievable. A problem I often encounter is something like this:</div><div><br></div><div>Service A calls service B. The unit tests for service A mock service B using statements like "expect(B).to receive(:call).with(x).and_return(y)". But in reality (due to refactors over time or misunderstandings between teammates), if you passed x to service B you'd get a return value of z, or it would raise an ArgumentError. So the unit tests for A are  only passing because they're describing an impossible situation.</div><div><br></div><div>Are there any good patterns for constraining the arguments and return values stated in mocks somehow? I'd love it if something would check that the inputs and outputs I specify when mocking service B have been "proven" to be accurate in my unit tests for B. That way my unit tests could give me some of the benefits of (slower, unwieldier) integration tests "for free". Or am I being hopelessly naive and misguided here?</div><div><br></div><div>Patrick</div><div><i>Mediocre developer. Failed composer. Fledgeling novelist (<a href="https://bedfordsquarepublishers.co.uk/book/hattie-brings-the-house-down/" target="_blank">https://bedfordsquarepublishers.co.uk/book/hattie-brings-the-house-down/</a>).</i></div><div><br></div></div>
_______________________________________________<br>
Chat mailing list<br>
<a href="mailto:Chat@lists.lrug.org" target="_blank">Chat@lists.lrug.org</a><br>
Archives: <a href="http://lists.lrug.org/pipermail/chat-lrug.org" rel="noreferrer" target="_blank">http://lists.lrug.org/pipermail/chat-lrug.org</a><br>
Manage your subscription: <a href="http://lists.lrug.org/options.cgi/chat-lrug.org" rel="noreferrer" target="_blank">http://lists.lrug.org/options.cgi/chat-lrug.org</a><br>
List info: <a href="http://lists.lrug.org/listinfo.cgi/chat-lrug.org" rel="noreferrer" target="_blank">http://lists.lrug.org/listinfo.cgi/chat-lrug.org</a><br>
</blockquote></div><br clear="all"><div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature"><div>------------------------</div>Andrew Premdas<div><a href="http://blog.andrew.premdas.org" target="_blank">blog.andrew.premdas.org</a></div></div>