Dependency Injection in .NET applications

18
Dependency Injection with .NET -OR- So you’ve decided to finally inject your dependencies -OR- Let’s test production scenarios so it doesn’t break

Transcript of Dependency Injection in .NET applications

Page 1: Dependency Injection in .NET applications

Dependency Injection with .NET

-OR-

So you’ve decided to finally inject your dependencies

-OR-Let’s test production scenarios so it

doesn’t break

Page 2: Dependency Injection in .NET applications

Overview

• What is DI?

• Why use DI?

• Where to use DI?

• How to do DI?

• C#, Legacy Code, and DI

– Interfaces, and Singletons, and Mocks, oh my!

Page 3: Dependency Injection in .NET applications

What is Dependency Injection

• A software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it.– Wikipedia

AppIoCDependency

ConcreteImplementation

ConcreteImplementation

ConcreteImplementation

Page 4: Dependency Injection in .NET applications

Dependency Inject – An Example

• Legacy code w/o DI

public class ReviewManager{

public ElasticSearchBackedReviewRepository ReviewsRepo

{get; set;}

public void GetReviewsByMovie( int movieId){

return ElasticSearchBackedReviewRepository.GetReviews(movieId);

}

}

• Legacy code w/o DI

public class ReviewManager{

public IReviewRepository ReviewsRepo {get; set;}

Public ReviewManager( IReviewRepository repo ){

ReviewsRepo = repo;

}

public void GetReviewsByMovie( int movieId){

return ReviewsRepo.GetReviews(moviedId);

}

}

AppIoCDependency

ConcreteImplementation

ConcreteImplementation

ConcreteImplementation

Page 5: Dependency Injection in .NET applications

Why Interfaces?

• Develop against a contract

– Business logic isn’t changed as new implementations are introduced

• Moq (for unit tests) requires interfaces or virtual methods.

• All wiring for dependencies can be performed in a centralized location

Page 6: Dependency Injection in .NET applications

Why Mocking?

• Allows testing any use case without the need for test data

– Mock a response that returns the use case you are testing (as opposed to test data in the DB)

– Null return values

– Timeout (web service clients)

– 0 reviews, 1 review, 10000 reviews

Page 7: Dependency Injection in .NET applications

When? Architecture Boundaries

App (Front End)App (Business Logic)

DB 1

DB 2

Service 1

Service 1

Service 1

Any time the app reaches out for data is a good candidate for dependency injection.

In this scenario, the clients we use to wrap the 2 databases and the 3 services would each implement an interface that could be mocked for unit tests

Page 8: Dependency Injection in .NET applications

When? Responsibility Boundaries

• Fandango Desktop/Mobile Web

– Reviews (Direct DB calls, Elastic search service)

– Movies (DB calls, movie service)

– Theaters (DB calls, Commerce service, old service, new API)

Page 9: Dependency Injection in .NET applications

DI via Delegates

• Initial Effort is low– No extra frameworks– unit test can define the mocked method

• Subsequent efforts are O(N)– One change per new mocked method

• Pros– Methods can be incorporated one at a time

• Cons– Each new method will require its own delegate and

new wiring.

Page 10: Dependency Injection in .NET applications

DI via Mock POCOs

• Initial Effort is medium– New class for each mock– Logic for mock states

• Subsequent efforts– Potential for new forks (and bugs) in mock POCO logic for

each use case

• Pros– Don’t need to learn a new framework.

• Cons– Supporting all use cases increases potential for bugs in the

mocks.– Some dependencies can’t be mocked

1

1 – With Moq, you can only mock methods defined in an interface or virtual methods.

Page 11: Dependency Injection in .NET applications

DI with Mocks (Moq)

• Initial Effort is high– Each unit test use case requires its own moq wrapper

per dependency

• Subsequent changes– Only changes to the contract require modifications to

your mocks

• Pros– Each tests clearly identifies its assumptions

• Cons– Lots of unit test code will just be setting up the

dependency

Page 12: Dependency Injection in .NET applications

Legacy Code: Static Methods

• Interfaces can’t have static methods

• Convert static methods to instance methods that conform to the (new) interface with a Singleton to expose the static instance.

Page 13: Dependency Injection in .NET applications

ScenarioProduction issue with unknown cause

• Scenario in production we can’t reproduce easily

• In a unit test, mock the underlying interface to the throw the same exception.

– At the very least, we’ll know how to stop this error from crashing the entire app and set up proper logging to identify potential causes

Page 14: Dependency Injection in .NET applications

ScenarioProduction issue with unknown cause

[Fact]

public void TestIndex_WebExceptionFromReviewManager()

{

InjectCookieCollectionDependency();

var mock = new Mock<IReviewManager>();

mock.Setup(rm => rm.GetEsReviewsByMovie(

It.IsAny<int>(),

It.IsAny<int>(),

It.IsAny<int>(),

It.IsAny<UserReviewSort>()))

.Throws(new WebException());

IReviewManager reviewManager = mock.Object;

using (var controller = new MovieController(reviewManager) { ControllerContext = new ControllerContext { HttpContext = new MockHttpContext() } })

{

MockHttpContext httpContext = configureContext(UserAgentValue);

controller.ControllerContext.HttpContext = httpContext;

ActionResult result = controller.Index(MovieIdAmericanBeauty);

Assert.IsType(typeof (ViewResult), result);

}

}

Page 15: Dependency Injection in .NET applications

ScenarioAnonymous + authenticated users

• Our apps identify a logged in user by the presence of a cookie. We can mock that!– See changes to MovieController with

ICustomerCookieProvider

Page 16: Dependency Injection in .NET applications

Strategies for Unit Testing

• New Projects

– TDD allows us to work with QA to identify good test cases before we start writing code

• Legacy Projects

– Don’t test something that’s been working in production for years

– Create unit tests to reproduce your bug. This will allow for immediate ROI on the tests

– Introduce tests for new features

Page 17: Dependency Injection in .NET applications

Side Effect (Good)

• Enforces the SOLID principlesSingle Responsibility [link]

If an interface crosses domains, you know to split themMultiple interfaces too many responsibilities for the class

Open/Closed Principle [link]

Your code is inherently open to extension

Liskov Substitution Principle [link]

Your mocks provide are substituted for the concrete implementations without code changes

Interface segregation Principle [link]

Interface contract hides helper methods

Dependency inversion principle [link]

This is what we’re talking about here

Page 18: Dependency Injection in .NET applications

Further Reading

• Martin Fowler (http://martinfowler.com/articles/injection.html)

• AutoFac IoC container (http://autofac.org/)

• AutoFac in action NuGet Gallery – DI by Environment