I have a package that provides an object with quite a lot of features owned by it.
Let us say the object is an HTTPServer
, and when the user initializes it by providing config and a request handler object, the server would be serving HTTP with added request observabilty, panic handling, and other in-house logics.
Now, if I want to test whether a handler's panic is properly handled, I would need to start the HTTP server, send a dummy request, and set up the observability; all which is unrelated to the panic handling logic. So, besides unit testing a feature of the object being cumbersome, there would also be a lot of reason why a test might fail (which if I understand correctly, is not a good property of a unit test),
Seeing other threads, a common idea would be to separate a particular subfeature to another (private) object, then unit test that object instead.
However, the sub-object would still be owned by the HTTPServer
object, and its implementation initialized by HTTPServer
during its initialization. To HTTPServer
, the sub-object would just be an implementation detail hidden away from the user, and the unit-test of the sub-object does not guarantee that the HTTPServer
is initialized and calls the sub-object properly. So, it cannot be mocked and we still need to retest the same functionality?
So, how do you decompose a big object for testing, and how do you use the decomposed objects in the big object's unit test if the whole functionality is still owned by the big object?
HTTPServer
constructor and run method. So even if I separate panic handling to another object, the user would still be unaware of this object and unable to pass it to theHTTPServer
object. So, the only one who knows and can build the panic handling object is stillHTTPServer
itself. How could I mock it then? – Bimo Adityarahman Oct 04 '23 at 03:31HTTPServer
. The user only cares that myHTTPServer
properly handle panic, and should not be bothered to initialize and provide the panic handler to create theHTTPServer
object.One possible idea maybe is to have a global panic handler object factory, to be used when
– Bimo Adityarahman Oct 04 '23 at 06:18HTTPServer
initialize the panic handler object, which could be replaced with a mock during testing. I am not sure though whether this is a correct application of the factory pattern.HTTPServer
is constructed is part of its contract in the sense that changing how the constructor is written would break users' codes.So in this case, I think panic handling object is entirely owned by the
– Bimo Adityarahman Oct 04 '23 at 14:38HTTPServer
. Its implementation is part of theHTTPServer
's, it is instantiated whenever anHTTPServer
is created, and destructed when theHTTPServer
are also.HTTPServer
default constructor is already aware of the default implementation of panic handler anyway. However, I don't see any added value in making it an actual global factory object, so I think I could just pass the constructor function directly as a factory. Thanks for your answer~ – Bimo Adityarahman Oct 06 '23 at 07:33