4 ms·
Testing Without Mocks: A Pattern Language
- wpannell 9y agoInteresting innovation. Coincides with 2nd Edition of Martin Fowler's "Refactoring," for which he chose to code in javascript. Interested in feedback from the mocking pioneers — Steve Freeman, Nat Pryce, Keiþ Braiþvvaite — and, of course, the father of TDD — Kent Beck. Is there sample code illustrating the pattern language?
- jdlshore 9y agoOther than the sample code in the article, my "WeeWikiPaint" project demonstrates most of the infrastructure patterns, particularly in its server code: https://github.com/jamesshore/lets_code_javascript/tree/episode614/src/server https://github.com/jamesshore/lets_code_javascript/tree/epis...
- JayBazuzi 9y ago> This can result in multiple tests failing when a bug is introduced. One way I approach this is "a bug will only cause one test to fail _at that level of abstraction_".
- JayBazuzi 9y ago> there's no need to create interfaces for dependencies I think in this context you're using `interface` in the way that C# uses it: a point of abstraction and indirection. I think you aren't using it in the API sense: the external surface of a thing. If that's correct, you may want to clarify your wording. Perhaps "abstract interface".
- JayBazuzi 9y agoI don't see ports-and-adapters (aka Hexagonal Architecture) mentioned. Is that because you don't like it / don't find it useful?
- jdlshore 9y agoI don't feel like I fully understand it.
- JayBazuzi 9y agoP-and-A makes Simulators possible, which are the one kind of test double I'm happy with. But your pattern language seems complete without it, so maybe I don't need it as much as I thought. Simulators fit right where you have Nullable Infrastructure. Having a null object and a real object, instead of a single object with both null and real behaviors is how I'd normally organize things. I write my Focused Integration tests so they can be run against both the real and the simulator, to ensure that they both agree on the contract. And just like the Nullable Infrastructure, I can use the simulator in tests. However, the real with with Ports-and-Adapters isn't the simulators (even though everyone notices that first). It's the way a good Port abstraction lets you organize your code well, writing it in terms that make sense in your domain, instead of in terms of the dependency. This is worth exploring further.
- jdlshore 9y agoOne of the key ideas of the Nullable Infrastructure is that all the semantics of the infrastructure class remain exactly the same other than disabling external communication. So any logic inside the infrastructure code still runs. This is important because we don't have broad integration tests. That's why the null code is inside the infrastructure, instead of using the classic Null Object pattern. It's also a major difference between it and classic test doubles. And perhaps your simulators?
- miensol 9y agoI like your article a lot. I do have a hard time to map the layers you've described (Application, Logic, Infrastructure) to a mobile/frontend/ui application. For instance in Android we've Activities/Fragments (and less commonly Views) as the entry points to the application code. From there we might have [View Models](https://developer.android.com/topic/libraries/architecture/viewmodel https://developer.android.com/topic/libraries/architecture/v...) and External API clients that are clearly infrastructure. From what I've seen it is common to call the External API clients from ViewModels directly. (This is so we can test more logic outside of Android runtime, AFAIK.) If I get the layers you've described we should instead let Activity/Fragment ask the ViewModel for some data and then use it to call the External API, correct?
- jdlshore 9y agoThanks, I'm glad you like it. I haven't done any Android work, so I'm not sure how the A-Frame Architecture applies. From my quick glance at the ViewModel documentation you linked, that looks like Application layer code: in the first example in that document, the `loadUsers()` call would turn around and call the actual a specific Infrastructure Wrapper responsible for communicating with the service that provides the users.