[LRUG] Joined-up unit tests
Galibier
apremdas at gmail.com
Mon Jun 10 07:06:18 PDT 2024
I have very opinionated views around this topic. Hopefully you'll find them
interesting.
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
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)
2) A consistent method for receiving the result of the call which includes;
a result boolean, and perhaps a modified thing that was sent.
This interface is somewhat clunky by nature so some strict rules should be
applied.
1) The service should follow a naming convention, i.e. have Service in its
class name.
2) A service should NEVER call another service.
3) Services should not do work, instead they should ask for work to be done
(generally by PORO's)
4) Services should follow a strict contract where all services return the
same thing (I call this a ServiceResult)
When you follow this pattern you get some major benefits
a) No need to ever mock a service
b) You can call services directly from integration tests to allow
integration tests to bypass the web layer when setting things up
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.
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.
This pattern works very well with BDD with each piece of behaviour ending
up having its own service.
I've dealt with a lot of difficult code where
i) Almost everything ends up being a Service (to the point where PORO's
were not being used at all)
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.
For me a Service calling another Service is a major anti-pattern, and in
codebases were I have control it is not allowed.
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.
All best :)
On Thu, 6 Jun 2024 at 11:59, Patrick Gleeson <patrick.c.gleeson at gmail.com>
wrote:
> Hi LRUG,
>
> I've been bitten by bad unit tests in the past (mostly ones I've written
> myself), and lean towards integration tests (a la
> https://x.com/rauchg/status/807626710350839808) when given the choice.
> But I want to believe good unit tests are achievable. A problem I often
> encounter is something like this:
>
> 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.
>
> 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?
>
> Patrick
> *Mediocre developer. Failed composer. Fledgeling novelist
> (https://bedfordsquarepublishers.co.uk/book/hattie-brings-the-house-down/
> <https://bedfordsquarepublishers.co.uk/book/hattie-brings-the-house-down/>).*
>
> _______________________________________________
> Chat mailing list
> Chat at lists.lrug.org
> Archives: http://lists.lrug.org/pipermail/chat-lrug.org
> Manage your subscription: http://lists.lrug.org/options.cgi/chat-lrug.org
> List info: http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>
--
------------------------
Andrew Premdas
blog.andrew.premdas.org
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lrug.org/pipermail/chat-lrug.org/attachments/20240610/e505bec1/attachment.htm>
More information about the Chat
mailing list