Isolation Techniques
Telerik Academy Alpha
UT
Table of contents
-
Testable code
-
Fakes, Mocks, Stubs
-
Mocking
Why and how?
Why?
- If we want to make a system
- Do we need all the components to work together?
- In order to do that do we actually want them to work separately?
- How do we ensure that?
- Do we need to have a monitor to prove that our computer works?
- Do we need a whole computer to prove that our mainboard works?
- Do we need a mainboard to be sure that our CPU works?
Why?
- In order to detach (isolate) from the dependencies in our code which are irrelevant to the working of the unit
SUT
EXECUTE
DEP1
DEP2
Why?
- In order to detach (isolate) from the dependencies in our code which are irrelevant to the working of the unit
- We create fake objects to replace the dependencies
SUT
EXECUTE
FAKE1
FAKE2
How?
- You could create a fake object which will replace the real one (Stub, Fake, Mock)
- Create a new class implementing the Interface expected to be passed
- It is a lengthy and error-prone technique
- Use an isolation framework
- Create a new class implementing the Interface expected to be passed
Testable Code
How to Write Testable Code
- Public API should work with interfaces, not implementation classes (IEnumerable vs. List)
// Bad code (strong dependency)
public Card[] Cards { get; private set; }
// Good code
public IList<ICard> Cards { get; private set; }
// Even better, depends on what we need
public ICollection<ICard> Cards { get; private set; }
Inversion of Control Pattern
- Every module can focus on what it is designed for
- Modules make no assumptions about what other systems do but rely on their contracts
- Replacing modules has no side effect on other module
- There is a decoupling of the execution of a certain task from implementation
Inversion of Control Pattern
-
Dependency Injection consists of:
- A dependent consumer
- A declaration of a component's dependencies, defined as interface contracts
- An injector that creates instances of classes that implement a given dependency interface on request
-
DI Containers:
- Ninject
- Unity
Example of un-testable code
public interface IViewBase {}
public interface IPresenterBase {}
public class MemoryLayoutView : IViewBase {}
public class MemoryLayoutPresenter : IPresenterBase
{
private MemoryLayoutView view = new MemoryLayoutView();
public MemoryLayoutPresenter() { }
}
Example of testable code
public interface IViewBase {}
public interface IPresenterBase {}
public class MemoryLayoutView : IViewBase {}
public class MemoryLayoutPresenter : IPresenterBase
{
private IViewBase view;
public MemoryLayoutPresenter(IViewBase myView)
{
this.view = myView;
}
}
public class Program {
public static void Main() {
InjectionContainer.Create<typeof(MemoryLayoutPresenter)>();
}
}
Fakes, Mocks, Stubs
Faking
- Makes Unit Testing more effective
- Avoid writing boring boilerplate code
- Avoid writing boring boilerplate code
- Isolate dependencies among units
- Asserts expectations for code quality
- Ex: Checks that a method is called only once
Fake vs Mock vs Stub
- Fake - objects actually have working implementations but with limited capabilities.
- Stub - provide canned answers to calls made during the test. May record information about calls.
- Mock - objects pre-programemd with expectations against we can assert
Isolation Frameworks
Handwritten vs with framework
- You can use inheritance to replace logic
- Maintainability - hard to achieve it
- Gets really complicated
- Could lead to errors
- With a framework, you can
- Create stub/mock objects
- Set the desired behaviour of the objects
- Verify (Assert) against the fake objects
Constrained vs Unconstrained frameworks
- Constrained frameworks usually work by generating code at runtime that inherits and overrides interfaces or base classes
- In .NET, constrained frameworks are unable to fake:
- static methods
- non-virtual methods
- nonpublic methods
- and more
Constrained vs Unconstrained frameworks
-
Unconstrained frameworks in .NET are using the so called profiling APIs.
- These APIs provide events on anything that happens during CLR code execution
- Profiling events happen on all the code, including static methods, private constructors, and even third-party code that doesn’t belong to you
Moq constrained framework
- Install from the NuGet package manager
- The most often used APIs:
- .Setup()
- .Verifiable()
- .Callback()
- .Returns()
- .Throws()
- It.Is(x => condition)
JustMock unconstrained* framework
- Install from the NuGet package manager
- Two versions:
- Free version (constrained)
- Paid version (unconstrained)
- The most often used APIs:
- .CallOriginal()
- .Returns()
- .DoInstead()
- .DoNothing()
- .Throw()
- Arg.Matches(x => condition)
Other frameworks
-
Constrained
- FakeItEasy
- nSubstitute
- RhinoMocks
-
Unconstrained
- Isolator
- MS Fakes
Questions?
[C# UT] Isolation Techniques
By telerikacademy
[C# UT] Isolation Techniques
- 1,095