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

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
  • 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,105