5 ms·
Thank you! I think I've got it now. So using the createNull version is very very similar to using a spy server, with the difference that with the null version t
by etamponi 5y ago
Thank you! I think I've got it now. So using the createNull version is very very similar to using a spy server, with the difference that with the null version the request is not sent at all.
If I can, may I ask you another question? How do you inject the createNull version in a test that does not have the architecture wrapper as a direct dependency? Do you manually unravel the dependencies? For example:
Test -> UpperController -> Controller -> ArchitectureWrapper
How do you test UpperController with a createNull version of ArchitectureWrapper?
Or is it necessary to always have the architecture as a direct dependency of the SUT? (As mandated by the A-frame architecture)
Thanks
- jdlshore 5y ago> similar to using a spy server I hadn't thought of it that way, but I think that's exactly correct. > How do you inject the createNull version in a test that does not have the [infrastructure] wrapper as a direct dependency? The short answer is that you create a chain of nullable objects. Infrastructure "pollutes" its callers, in that having intrastructure in the dependency chain exposes you to the downsides of infrastructure (external state, network calls, unreliability, etc.). So I consider deep dependencies on infrastructure a design smell. Hence A-Frame Architecture. That said, design is all about tradeoffs, and sometimes a deep dependency on infrastructure is better than the alternatives. Your example of UpperController and Controller is something I'd prefer to avoid, but it's normal for me to have "high-level" infrastructure that depends on "low-level" infrastructure. For example, let's say we implement a logging wrapper. Under the covers, it depends on CommandLine, a wrapper for stdout. When used by Controller, the chain looks like this: Controller --> Log --> CommandLine --> process.stdout CommandLine exposes trackStdout()¹, which allows you to record what was written to stdout, and createNull(), which allows you to create a CommandLine that doesn't actually write to stdout. ¹trackStdout() is a more sophisticated alternative to the "Send Events" and "Send State" (getLastOutput) patterns mentioned in the 2018 article. But Controller isn't aware of CommandLine. Controller is only aware of Log. Log's use of CommandLine is an encapsulated implementation detail that's hidden from the rest of the world. So Log also exposes methods to help you test. It has trackOutput(), which allows you to record what was written to the log, and createNull(), which allows you to create a Log that doesn't actually write to the log. Importantly, here, trackOutput() isn't just calling CommandLine.trackOutput(). It's providing a higher-level abstraction that knows how to deal with structured logs, timestamps, and stack traces. So in the test of Controller, your code looks like this (JavaScript): it("logs stuff", () => { const log = Log.createNull(); const logOutput = log.trackOutput(); const controller = new Controller(log); controller.doStuff(); assert.deepEqual(logOutput, [ { message: "my expected log line 1", detail: "some detail" }, { message: "my expected log line 2", detail: "some other detail" }, ]); }); This isn't a lot of work for Log, because it can use the abstractions provided by CommandLine. In particular, its factory methods look like this: static create() { return new Log(CommandLine.create()); } static createNull() { return new Log(CommandLine.createNull()); } I have a video for this example at [2] and the source code is at [3]. [2] https://www.jamesshore.com/v2/projects/lunch-and-learn/testable-logs https://www.jamesshore.com/v2/projects/lunch-and-learn/testa... [3] https://github.com/jamesshore/livestream/blob/2020-08-11-end/src/infrastructure/log.js https://github.com/jamesshore/livestream/blob/2020-08-11-end...
- etamponi 5y agoClear, thank you! In the meantime, I've started watching the videos and they're excellent. I've found answers to many other questions there. Thanks for putting all of this together!