<div dir="ltr"><div dir="ltr">Apologies for late reply.<div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, 18 Jun 2024 at 14:49, Tim Diggins <<a href="mailto:tim@red56.uk">tim@red56.uk</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 Andrew<div><br></div><div>Your rules are interesting and *potentially* appealing. Two questions: </div><div><br></div><div>> 3) Services should not do work, instead they should ask for work to be done</div><div>What precisely do you mean by the difference between "do work" and "ask for work to be done". My guess is that this means - a service should at most instantiate a PORO and call a method on it.</div></div></blockquote><div><br></div><div>Yes pretty much this but ...</div><div><br></div><div>-  if there is some cross cutting concern like auditing service calls, then the service could do that as well. </div><div>- if multiple PORO's need to be called and results collated, then perhaps this could be done in the service, or perhaps delegated to another PORO.</div><div> </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"><div><br></div><div>> 4) Services should follow a strict contract where all services return the same thing (I call this a ServiceResult)</div><br><div>Do you intend this to mean every service returns an instance of one unique class  ServiceResult and have no behavioural differences between each other? (so ServiceX's return and ServiceY's return are the same type). Why does this help? (rather than them being potentially ServiceXResult, ServiceYResult)?</div><div><br></div></div></blockquote><div><br></div><div>Yes, generally this interface is a boolean to indicate success and failure, and then something else. The something else could be relatively specific e.g. a model object, or something vaguer depending on application design and what your approach to typing is. If you need different types of ServiceResult you can always subclass.</div><div><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"><div></div><div>thanks</div><div><br></div><div>Tim</div></div></blockquote><div><br></div><div>Your welcome</div><div><br></div><div>All best</div><div><br></div><div>Andrew</div><div><br></div><div><br></div><div>------------------------ </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"><div><div><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><br></div></div></div></div></div></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, 10 Jun 2024 at 15:10, Galibier <<a href="mailto:apremdas@gmail.com" target="_blank">apremdas@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">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" target="_blank">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>
_______________________________________________<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>
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></div>