Corridor
A Coreader-like Dependency Injection μFramework
Why
In order to write tests we must substitute parts of our code that we do not have control over such as:
- Network
- File system
- Creating dates
- Keychain
We need to substitute them in tests in order to verify assumptions.
The purpose of Corridor is to:
- Provide a common interface for things that need to be replaced in TestCases
- Simplify setup in TestCases without manually providing mocks etc
- Transparently provide the current context to all your Types
- Separate any kind of test related logic from production code
In an ideal World a Coeffect is under control.
The Date in the above example is out of control.
Running a test for that Controller will always result in a different Date.
In that case the Date is just a placeholder for any Coeffect.
Corridor tries to solve this problem by taking the concept of a Coreader and turning it into a Swift friendly implementation via protocols and a single property.
What will it look like?
The idea behind Corridor was part of my talk at the Functional Swift Conference 2017 in Berlin.
Usage
Implement a Protocol
Either one of the two protocols provided by Corridor: HasInstanceContext
or HasStaticContext
.
Or any convenience protocol that extends one of those.
Add a Property
Any type that needs access to an injected value also needs to know how to resolve it. This is done by providing a property called resolve.
By default it should be set to var resolve = `default`
.
Why the backticks?
default is a swift keyword and by using the backticks the property looks
more config-ish.
Setup Context
A base protocol that defines your dependencies:
Context Implementation
An implemention of a Context.
Usually we use two implementations.
One for the running application, one for the test cases.
Resolver
In order to provide the default resolver you must extend the base protocol in Corridor. This will provide a static variable called default
of Type Resolver to your Type in order to provide access.
This extension is done once in your app.
The visibility of any property in the context is controlled by extending either HasInstanceContext
or HasStaticContext
or any derived protocol.
By using protocols we can constrain access in a granular way. Additionally it allows for the injection of functions.
See example CorridorDemo.playground.
Changing the Context
In your actual code everything resolves to the DefaultContext
.
But in your Tests you need to make sure to switch to the mock context.
The simplest way is:
Setting up the context in the Tests can easily be simplified by making the TestCase itself Context aware. Additionally you can build functions on top of that to make instantiation automagically have the correct Context.
Examples from Playground
See the provided Playground in the workspace.
Intro
Test
Api
This example combines a Reader
composition for chained REST calls.
Defining the Api
in AppContext
and by implementing ContextAware
it
will have access to the current context.
Therefore we don't need to pass additional params to the network calls, and
it is ensured that all injected valus are correctly resolved.
FAQ
What if a property needs to be resolved to the context?
You can use lazy var
to resolve properties directly.
See Tests.
Installation
Carthage
To integrate Corridor into your project using Carthage, add to your Cartfile
:
See Carthage for further inststructions.
Requirements
Swift 4
Credits & License
Corridor is owned and maintained by Symentis GmbH.
All modules are released under the MIT license. See LICENSE for details.