You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As golang does not have the keyword "implements", interfaces are resolved at run time. This opens golang up to the principle "Accept Interfaces, return Structs" which is a very powerful technique for decoupling and as a result, creates injection points for mocking.
I propose refactoring receptor using this technique. Here is what it could look like:
Each package will be decoupled from each other via a local package interface
Package interfaces could follow a naming convention outlining the relationship they represent
Fields outside the calling package will no longer be directly accessible, but instead, must be retrieved via Getters
The scope of this technique should not be limited to inter-package relationships, but should also be expanded to include 3rd party libraries and local functions/structs/objects.
Here is an example of what this would look like using the "Workceptor" and "Netceptor" relationship. Currently within the "Workceptor" struct their is a "Netceptor" field. I propose replacing this with an interface like so:
typeWorkceptorsNetceptorinterface {
NodeID() stringAddWorkCommand(typeNamestring, verifySignaturebool) errorGetClientTLSConfig(namestring, expectedHostNamestring, expectedHostNameType netceptor.ExpectedHostnameType) (*tls.Config, error)
GetLogger() *logger.ReceptorLoggerDialContext(ctx context.Context, nodestring, servicestring, tlscfg*tls.Config) (*netceptor.Conn, error)
}
// Workceptor is the main object that handles unit-of-work management.typeWorkceptorstruct {
ctx context.ContextCancel context.CancelFuncncWorkceptorsNetceptordataDirstringworkTypesLock*sync.RWMutexworkTypesmap[string]*workTypeactiveUnitsLock*sync.RWMutexactiveUnitsmap[string]WorkUnitSigningKeystringSigningExpiration time.DurationVerifyingKeystring
}
Why do this?
Decoupling and injection.
Lets take testing as an example, currently their is no way to expose "Workceptor" as a unit because it is tightly coupled to "Netceptor". This means that any test or "Workceptor" functions are automatically integration tests, they will always require "Netceptor" objects to be set up and return correct values.
So if we were to follow the above suggestion, this interface will give us an injection point in which we can mock out all "Netceptor" interactions. Couple this with the mockgen tool and we can easily create and control the responses from "Netceptor" in our unit tests. Here is what that could look like:
// set up mocksctrl:=gomock.NewController(t)
deferctrl.Finish()
mockNetceptor:=mock_workceptor.NewMockWorkceptorsNetceptor(ctrl)
logger:=logger.NewReceptorLogger("")
// During workceptor setup, netceptor functions are calledmockNetceptor.EXPECT().GetLogger().AnyTimes().Return(logger)
mockNetceptor.EXPECT().NodeID().Return("test")
// set up workceptor using the mocked netceptorw, err:=workceptor.New(ctx, mockNetceptor, "/tmp")
iferr!=nil {
t.Errorf("Error while creating Workceptor: %v", err)
}
// test workceptors "Register Worker" functionmockNetceptor.EXPECT().AddWorkCommand(gomock.Any(), gomock.Any()).Return(nil)
w.RegisterWorker("testType", workFunc, false)
As you can see from above, using the "EXPECT()" will allow us to force passing and failing responses, allowing us to control a "unit" of work for testing.
All feedback welcome. Let me know your thoughts
The text was updated successfully, but these errors were encountered:
As golang does not have the keyword "implements", interfaces are resolved at run time. This opens golang up to the principle "Accept Interfaces, return Structs" which is a very powerful technique for decoupling and as a result, creates injection points for mocking.
I propose refactoring receptor using this technique. Here is what it could look like:
Here is an example of what this would look like using the "Workceptor" and "Netceptor" relationship. Currently within the "Workceptor" struct their is a "Netceptor" field. I propose replacing this with an interface like so:
Why do this?
Decoupling and injection.
Lets take testing as an example, currently their is no way to expose "Workceptor" as a unit because it is tightly coupled to "Netceptor". This means that any test or "Workceptor" functions are automatically integration tests, they will always require "Netceptor" objects to be set up and return correct values.
So if we were to follow the above suggestion, this interface will give us an injection point in which we can mock out all "Netceptor" interactions. Couple this with the mockgen tool and we can easily create and control the responses from "Netceptor" in our unit tests. Here is what that could look like:
As you can see from above, using the "EXPECT()" will allow us to force passing and failing responses, allowing us to control a "unit" of work for testing.
All feedback welcome. Let me know your thoughts
The text was updated successfully, but these errors were encountered: