Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Thursday, 12 June 2014

http://www.manning.com/seemann/

“I think Dependency Injection is like sauce béarnaise.  It’s assumed to be difficult and so few employ it”

DI is a set of software design principles and patterns that enable us to develop loosely coupled code

http://blog.ploeh.dk/2014/06/10/pure-di/ – an updated article on Constructor Based DI

Overview

2.5 years it took Mark to write this book.  It is concise, yet deep.  I’ve done an hours overview of the book, and looked at the source code (which doesn’t compile nicely in VS2013 but am sure will be ok).  It looks like a book which is much more than an DI book, and covers ‘good code’.

A DI Tasting Menu – 1.1 Writing Maintainable Code

DI may seem like a rather backward way of coding – there is much FUD (Fear, Uncertainty and Doubt) involved.  We must learn about the purpose of DI:

Writing Maintainable Code

Ultimately the purpose of programming is to deliver working software as efficiently as possible

You’ll find yourself maintaining and extending code

One of the ways to make maintainable code is through loose coupling

  • Deliver working software efficiently
  • Write maintainable code
  • Loose coupling (makes code extensible which makes it maintainable)
  • DI enables loose coupling

Hairdryers and Plugs

Through the use of sockets and plugs – a hair dryer can be loosely coupled to the wall outlet

  • Replace 1 end without changing the other
  • Liskov Substitution Principle – replace one implementation of an interface with another without breaking client or implementation
  • If unplug from wall, neither should break down.  “Null Object design pattern”

UPS

Way of intercepting one implementation with another of the same interface is known as the Decorator design pattern

Power Strip

Composite design pattern

Power Adapter

Adapter design pattern – match 2 related yet separate interfaces

Hello DI

In c:\dev\test\hellodi  page 46

class Program {
    static void Main(string[] args) {
        IMessageWriter writer = new ConsoleMessageWriter();
        var salutation = new Salutation(writer);
        salutation.Exclaim();
    }
}

public interface IMessageWriter {
    void Write(string message);
}

public class ConsoleMessageWriter : IMessageWriter {
    public void Write(string message) {
        Console.WriteLine(message);
    }
}

public class Salutation {
    private readonly IMessageWriter writer;

    public Salutation(IMessageWriter writer) {
        if (writer == null) {
            throw new ArgumentNullException("writer");
        }

        this.writer = writer;
    }

    public void Exclaim() {
        this.writer.Write("Hello DI!");
    }
}

2014-07-08 07.46.36

The Salutation class depends on a custom interface called IMessageWriter, and it requests an instance of it through it’s constructor.

The ConsoleMessageWriter class wraps the BCL Console class.  This is a simple application of the Adapter pattern as we are getting 1 Interface to work with another.

“You may be wondering about the benefit of replacing a single line of code with two
classes and an interface with a total line count of 11, and rightly so. There are several
benefits to be harvested from doing this.”

“In this example, DI adds an overhead of 1,100%, but, as complexity increases from one line of code to tens of thousands, this overhead diminishes and all but disappears.”

Benefits Gained from Loose Coupling

  • Late binding
    • Services can be swapped with other services
    • Valuable in standard software but perhaps less so in enterprise where runtime env is well defined
  • Extensibility
    • Code be be extended and reused in ways not planned for
  • Parallel Dev
    • Code be be dev’d in parallel
    • Valuable in large complex applications
  • Maintainability
    • Classes with clearly defined responsibilities are easier to maintain
  • Testability
    • Classes can be tested

Late Binding

static void Main(string[] args) {
    //IMessageWriter writer = new ConsoleMessageWriter();

    // Late binding - using reflection to create instance of IMessageWriter
    // without knowing concrete type at compile time
    var typeName = ConfigurationManager.AppSettings["messageWriter"];
    var type = Type.GetType(typeName, true);
    var writer = (IMessageWriter) Activator.CreateInstance(type);
        
    var salutation = new Salutation(writer);
    salutation.Exclaim();
}

Extensibility

Want to add security by only allowing authenticated users to write messages.  So can add the feature without changing any of the existing features.

Decorator - way of intercepting one implementation with another of the same interface

Decorate the old ConsoleMessageWriter instance with the new SecureMessageWriter class.  The salutation class is unmodified because it only consumes the IMessageWriter interface

// Decorator - way of intercepting one implementation
// with another of the same interface
public class SecureMessageWriter : IMessageWriter
{
    private readonly IMessageWriter writer;
       
    public SecureMessageWriter(IMessageWriter writer){
        if (writer == null) {
            throw new ArgumentNullException("writer");
        }
        this.writer = writer;
    }

    public void Write(string message){
        if (Thread.CurrentPrincipal.Identity.IsAuthenticated){
            this.writer.Write(message);
        }
    }
}

and call it differently

IMessageWriter writer = new SecureMessageWriter(new ConsoleMessageWriter());
var salutation = new Salutation(writer);
salutation.Exclaim();

2014-07-08 07.46.50

Loose coupling enables you to write code which is open for extensibility but closed for modification.  Open/Closed principle.  Only place where you need to modify the code is at the app’s entry point – we call this the composition root

Parallel Development

SecureMessageWriter and ConsoleMessageWriter don’t depend on each other so could be dev’d in parallel.  All that is needed is a common interface

Maintainability

As the responsibility of each class becomes clearly defined and constrained, maintenance becomes easier.

Single Responsibility Principle – each class should have only a single responsibility

Adding new features becomes simpler because it’s clear where changes should be made.  More often than not, we don’t even need to change existing code, but can add new classes, and recompose the app.  This is Open/Closed Principle again

Testability

Test Doubles – implementations of dependencies that act as stand-ins for real or intended implementations

  • Stubs
  • Mocks
  • Fakes

Ctrl U L – Run all unit tests in sln (R#) – and auto display window
Ctrl U D – Debug unit test
Ctrl U R – Run unit test(s)

public class SalutationTests {
    [Fact]
    public void InjectedMessageWriter_Exclaim_WriteCorrectMessage() {
        var writerMock = new Mock<IMessageWriter>();
        var sut = new Salutation(writerMock.Object);
            
        sut.Exclaim();

        // Verify Salutation.Exclaim method has passed correct message to injected
        // Writer by calling the Write method
        writerMock.Verify(w => w.Write("Hello DI!"));
    }
}

Interesting tests for his SecureMessageWriter on identity

Seams

We have a seam between ConsoleMessageWriter and Salutation

Stable Dependencies

  • Class or module already exists
  • Expect that new versions won’t contain breaking changes
  • Never expect to have to replace the class or module

Most types in the BCL as stable.  DI containers.  Specialised libraries. 

Volatile Dependencies

Introducing seams is extra work

A dependency should be considered volatile if any of the following are true:

  • Set up and configure a runtime environment eg a db.  We hide the db behind a seam, so can replace it by automated unit tests.  Message Queues, web services, file system
  • Dependency doesn’t exist yet
  • Dependency isn’t installed on all machines eg expensive 3rd party library
  • Dependency contains nondeterministic behaviour eg random numbers, date and time

DI Scope

We break up responsibilities into different classes.  One responsibility we take away from classes is the task of creating dependencies.

SRP – classes should only deal with their given area of responsibility without concerning themselves with how the dependencies are created

Object Composition

This is often the foremost motivation for introducing DI.

image

Modules can be composed into applications.  Only aspect discussed in Martin Fowler’s original article in 2004 (IoC and DI pattern)

Lifetime Management

What is objects want to share an instance of another object eg a db connection

Interception

Can modify a dependency before passing onto the class consuming them eg SecureMessageWriter above.  Example of decorator pattern.  This is moving along towards Aspect-Oriented Programming.

With interception we can apply Cross-Cutting concerns eg logging, auditing, access control, validation etc..

DI in 3D

  • Object composition (DI started out as a series of patterns aimed at solving this)
  • Object lifetime
  • Interception

Summary

DI is the best way to enable loose coupling – an important part of maintainable code

The benefits we can reap from loose coupling aren’t immediately apparent – they become visible over time as the complexity of the code base grows.

DI must be pervasive.  You can’t easily retrofit loose coupling onto an existing codebase

| | # 
# Tuesday, 13 September 2011
( Autofac | Moq | SpecFlow )

Source is here: https://github.com/techtalk

SpecFlow aims at bridging the communication gap between domain experts and developers by binding business readable behavior specifications to the underlying implementation.

http://stackoverflow.com/questions/3443302/specflow-bdd-examples

https://github.com/techtalk/SpecFlow/wiki/Getting-Started – lots of links to tutorials and videos

image

App has only a few screens.  To see all books, you must search for nothing.

image

Testing the HomeController of the MVC layer

  • It returns a result to the default view
  • It produces a list of books in the Model (which the view will use).. using a faked out Service layer which just returns the 2 books

image

Details

image

image 

Code Snippet
        [TestMethod]
        public void WhenValidBookId_ShouldReturnViewWithBookForTheId()
        {
            // given
            var book = new Book();
            catalogServicesFake.Setup(m => m.GetBookDetails(1)).Returns(book);

            // when
            var result = controller.Details(1);

            // then
            var model = result.GetModel<DetailsModel>();
            Assert.AreEqual(book, model.Book);
        }

        [TestMethod, ExpectedException(typeof(ArgumentException))]
        public void WhenInvalidBookId_ShouldThrowArgumentException()
        {
            // given
            catalogServicesFake.Setup(m => m.GetBookDetails(1)).Returns((Book)null);

            // when
            controller.Details(1);

            // then: ArgumentException
        }
  • First test just tests that the /catalog/details/1   actually returns a viewresult  ie something happens
  • second just fakes a book
  • third is testing the null catch – this is actually testing business logic, and not just things are wired up right.
Code Snippet
public ActionResult Details(int id)
        {
            var book = catalogServices.GetBookDetails(id);
            if (book == null)
                throw new ArgumentException("Invalid book id", "id");

            var ratings = this.ratingsGateway.RetrieveRatings(book);

            decimal averageRating = ratingCalculator.CalculateRating(ratings ?? new Rating[0]);

            // US1_2_1: Call recommendationsGateway and process result
            string recommendedBooks = "";

            return View(new DetailsModel {
                Book = book,
                Rating = averageRating,
                RecommendedBooks = recommendedBooks
            });
        }

Search

Unit tests at this level (MVC) are really just testing something happens and doesn’t blow up.  And guard conditions are working ie null checks.

Code Snippet
namespace BookShop.UnitTests.CatalogControllerTests {
    [TestClass]
    public class SearchTests {
        private CatalogController controller;
        private Mock<ICatalogServices> catalogServicesFake;

        [TestInitialize]
        public void Setup() {
            catalogServicesFake = new Mock<ICatalogServices>();
            controller = new CatalogController(
                catalogServicesFake.Object,
                new Mock<IRatingsGateway>().Object,
                new Mock<IRatingCalculator>().Object,
                new Mock<IRecommendationsGateway>().Object);
        }

        [TestMethod]
        public void WhenSearchIsSuccessful_ShouldReturnViewResultToListView() {
            // given
            catalogServicesFake.Setup(m => m.Search("searchTerm")).Returns(new List<Book>());

            // when
            var result = controller.Search("searchTerm");

            // then
            Assert.AreEqual("List", ((ViewResult)result).ViewName);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void WhenArgumentIsNull_ShouldThowException() {
            // given

            // when
            controller.Search(null);

            // then: ArgumentNullException
        }



        [TestMethod]
        public void WhenSearchResultIsEmpty_ShouldReturnViewWithEmptyList() {
            // given
            catalogServicesFake.Setup(m => m.Search("nonMatchingSearchTerm")).Returns(new List<Book>());

            // when
            var result = controller.Search("nonMatchingSearchTerm");

            // then
            var model = result.GetModel<List<Book>>();
            Assert.AreEqual(0, model.Count);
        }

        [TestMethod]
        public void WhenSearchResultIsNotEmpty_ShouldReturnViewWithListOfBooks() {
            // given
            var book1 = new Book();
            var book2 = new Book();
            catalogServicesFake.Setup(m => m.Search("searchTerm")).Returns(new List<Book> { book1, book2 });

            // when
            var result = controller.Search("searchTerm");

            // then
            var model = result.GetModel<List<Book>>();
            CollectionAssert.AreEquivalent(new List<Book> { book1, book2 }, model);
        }
    }
| | # 
# Tuesday, 16 August 2011

Business owner must choose the first stories to be developed in the first iteration.

“A user should be able to add a new type of item to the application”

Features:

  • An item type entity and a persistence layer
  • An item type domain service that can provide a list of item types in the data store
  • A user interface to enable business users to list existing item types and create new ones

Inside out / outside in… we are doing the former ie starting with the Repository layer.

http://nbehave.codeplex.com/releases/view/68223

Code Snippet
public class when_working_with_the_item_type_repository : Specification {
    }

    public class and_saving_a_valid_item_type : when_working_with_the_item_type_repository {
        private int _result;
        private IItemTypeRepository _itemTypeRepository;
        private ItemType _testItemType;
        private int _itemTypeId;

        protected override void Because_of() {
            _result = _itemTypeRepository.Save(_testItemType);
        }
        
        protected override void Establish_context() {
            base.Establish_context();

            var randomNumberGenerator = new Random();
            _itemTypeId = randomNumberGenerator.Next(32000);

            // making a mock ISessionFactory
            // which just returns the random number
            // when an ItemType is passed in and saved
            var sessionFactory = new Mock<ISessionFactory>();
            var session = new Mock<ISession>();
            session.Setup(s => s.Save(_testItemType))
                            .Returns(_itemTypeId);

            sessionFactory.Setup(sf => sf.OpenSession())
                            .Returns(session.Object);

            // injecting in our mock sessionFactory
            _itemTypeRepository = new ItemTypeRepository(sessionFactory.Object);
        }

        [Test]
        public void then_a_valid_item_type_id_should_be_returned() {
            _result.ShouldEqual(_itemTypeId);
        }
    }
Specification inherits off SpecBase which is NBehave.Spec.Nunit.    So basically Nunit with BDD style sugar.

Using Moq to Mock out the ORM (Fluent NHibernate).

Code Snippet
public interface IItemTypeRepository {
        int Save(ItemType _testItemType);
    }

    public class ItemTypeRepository : IItemTypeRepository {
        private ISessionFactory _sessionFactory;

        public ItemTypeRepository(ISessionFactory sessionFactory) {
            _sessionFactory = sessionFactory;
        }

        public int Save(ItemType itemType) {
            int id;
            using (var session = _sessionFactory.OpenSession()) {
                id = (int)session.Save(itemType);
                session.Flush();
            }
            return id;
        }
    }

Second test and Refactoring

Code Snippet
public class when_working_with_the_item_type_repository : Specification {
        protected IItemTypeRepository _itemTypeRepository;
        protected Mock<ISessionFactory> _sessionFactory;
        // protected so can restub in each test the required behaviour
        protected Mock<ISession> _session;

        protected override void Establish_context() {
            base.Establish_context();
            // Mock out FNH
            _sessionFactory = new Mock<ISessionFactory>();
            _session = new Mock<ISession>();

            _sessionFactory.Setup(sf => sf.OpenSession())
                            .Returns(_session.Object);

            _itemTypeRepository = new ItemTypeRepository(_sessionFactory.Object);
        }
    }

    // second test
    public class and_saving_an_invalid_item_type : when_working_with_the_item_type_repository {
        private Exception _result;

        protected override void Because_of() {
            try {
                _itemTypeRepository.Save(null);
            }
            catch (Exception exception) {
                _result = exception;
            }
        }

        protected override void Establish_context() {
            base.Establish_context();
            // the mock knows that when a null is passed
            // instead of an ItemType then throw ArgumentNullException
            _session.Setup(s => s.Save(null))
                            .Throws(new ArgumentNullException());
        }

        [Test]
        public void then_an_argument_null_exception_should_be_raised() {
            _result.ShouldBeInstanceOfType(typeof(ArgumentNullException));
        }
    }

    // first test
    public class and_saving_a_valid_item_type : when_working_with_the_item_type_repository {
        private int _result;
        private ItemType _testItemType;
        private int _itemTypeId;

        protected override void Because_of() {
            _result = _itemTypeRepository.Save(_testItemType);
        }
        
        protected override void Establish_context() {
            base.Establish_context();

            var randomNumberGenerator = new Random();
            _itemTypeId = randomNumberGenerator.Next(32000);

            // when passed an ItemType, return the random number
            _session.Setup(s => s.Save(_testItemType))
                            .Returns(_itemTypeId);
        }

        [Test]
        public void then_a_valid_item_type_id_should_be_returned() {
            _result.ShouldEqual(_itemTypeId);
        }
    }

Now have 2 unit tests.. which are testing the repository

  • Saving a valid item type returns newly created id
  • Saving an invalid item type throws an ArgumentNullException

The repository is being tested using Mocks of the FNH Save method.  Because we’re using FNH’s interfaces it should just work (after putting in config files and creating the DB!).

| | # 
# Monday, 15 August 2011

an Entity could be a Person object.

Code Snippet
// app is comprised of various services
    // which have entities (components)

    public class BusinessService {
        private readonly string _databaseConnectionString = ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
        private readonly string _webServiceAddress = ConfigurationManager.AppSettings["MyWebServiceAddress"];
        private readonly LoggingDataSink _loggingDataSink;
    
        private DataAccessComponent _dataAccessComponenet;
        private WebServiceProxy _webServiceProxy;
        private LoggingComponent _loggingComponent;

        public BusinessService() {
            _loggingDataSink = new LoggingDataSink();
            _loggingComponent = new LoggingComponent(_loggingDataSink);
            _webServiceProxy = new WebServiceProxy(_webServiceAddress);
            _dataAccessComponenet = new DataAccessComponent(_databaseConnectionString);
        }
    }

refactoring for constructor based DI:

Code Snippet
    public class BusinessServiceWithDI {
        private IDataAccessComponent _dataAccessComponent;
        private IWebServiceProxy _webServiceProxy;
        private ILoggingComponent _loggingComponent;

        public BusinessServiceWithDI(IDataAccessComponent dataAccessComponent, IWebServiceProxy webServiceProxy, ILoggingComponent loggingComponent) {
            _dataAccessComponent = dataAccessComponent;
            _webServiceProxy = webServiceProxy;
            _loggingComponent = loggingComponent;
        }
    }

Can now inject whatever I like as long as it implements the required interface eg a ‘test’ database.  So mock instances of these services can be injected for testing.

DI frameworks give us an alternative to Factory Patterns which can get unwieldy.

Allow us to easily define dependencies and rules for creating the concrete objects.

Ninject

Code Snippet
public class BusinessService {
        private IDataAccessComponent _dataAccessComponent;
        private IWebServiceProxy _webServiceProxy;
        private ILoggingComponent _loggingComponent;

        public BusinessService(IDataAccessComponent dataAccessComponent, IWebServiceProxy webServiceProxy, ILoggingComponent loggingComponent) {
            _dataAccessComponent = dataAccessComponent;
            _webServiceProxy = webServiceProxy;
            _loggingComponent = loggingComponent;
        }
    }

then:

Code Snippet
[Test]
        public void ShouldBeAbleToGetBusinessServiceFromNinject() {
            BusinessService actual;
            var kernel = new StandardKernel(new CoreModule());
            actual = kernel.Get<BusinessService>();
            Assert.IsNotNull(actual);
        }

so can get a BusinessService and all of its dependencies injected for us.

Code Snippet
public class CoreModule : NinjectModule{
        public override void Load() {
            // any requests to ninject for a class that implements ILoggingDataSink should return
            // a concrete instance of LoggingDataSink
            Bind<ILoggingDataSink>().To<LoggingDataSink>();
            Bind<ILoggingComponent>().To<LoggingComponent>();
            Bind<IDataAccessComponent>().ToProvider(new DataAccessComponentProvider());
            Bind<IWebServiceProxy>().ToProvider(new WebServiceProxyComponentProvider());
        }
    }

    public class DataAccessComponentProvider : Provider<IDataAccessComponent> {
        protected override IDataAccessComponent CreateInstance(IContext context) {
            var databaseConnectionString = ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
            return new DataAccessComponent(databaseConnectionString);
        }
    }

    public class WebServiceProxyComponentProvider : Provider<IWebServiceProxy> {
        protected override IWebServiceProxy CreateInstance(IContext context) {
            var webServiceAddress = ConfigurationManager.AppSettings["MyWebServiceAddress"];
            return new WebServiceProxy(webServiceAddress);
        }
    }

Service and Repository

Code Snippet
public interface IPersonService {
        Person GetPerson(int personId);
    }

    public class PersonService : IPersonService {
        private readonly IPersonRepository _personRepository;

        public PersonService(IPersonRepository personRepository) {
            _personRepository = personRepository;
        }
        public Person GetPerson(int personId) {
            return _personRepository.GetPerson(personId);
        }
    }

    public class Person {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public interface IPersonRepository {
        Person GetPerson(int personId);
    }

    public class PersonRepository : IPersonRepository {
        private readonly IList<Person> _personList;

        public PersonRepository() {
            _personList = new List<Person> {
                                            new Person{Id = 1, FirstName = "John", LastName="Doe"},
                                            new Person{Id = 2, FirstName = "Richard", LastName="Roe"},
                                            new Person{Id = 3, FirstName = "Amy", LastName="Adams"}
                                            };
        }

        public Person GetPerson(int personId) {
            var person = _personList.Where(p => p.Id == personId).FirstOrDefault();
            return person;
        }
    }

tests:

Code Snippet
[TestFixture]
    public class PersonServiceTests {
        [Test]
        // not truly a unit test as it breaks boundaries between PersonService and PersonRepository
        // we should pass a mock repository into the service.
        public void ShouldBeAbleToCallPersonServiceAndGetPerson() {
            var expected = new Person { Id = 1, FirstName = "John", LastName = "Doe" };
            var kernel = new StandardKernel(new CoreModule());
            var personService = kernel.Get<PersonService>();
            var actual = personService.GetPerson(expected.Id);

            Assert.AreEqual(expected.Id, actual.Id);
            Assert.AreEqual(expected.FirstName, actual.FirstName);
            Assert.AreEqual(expected.LastName, actual.LastName);
        }

        [Test]
        // not using ninject
        // using moq
        public void ShouldBeAbleToCallPersonServiceAndGetPersonWithMock() {
            var expected = new Person { Id = 1, FirstName = "Bob", LastName = "Smith" };
            var personRepositoryMock = new Mock<IPersonRepository>();
            personRepositoryMock
                    .Setup(pr => pr.GetPerson(1))
                    .Returns(new Person { Id = 1, FirstName = "Bob", LastName = "Smith" });
            var personService = new PersonService(personRepositoryMock.Object);

            var actual = personService.GetPerson(expected.Id);

            Assert.AreEqual(expected.Id, actual.Id);
            Assert.AreEqual(expected.FirstName, actual.FirstName);
            Assert.AreEqual(expected.LastName, actual.LastName);
        }
    }
| | # 
# Friday, 12 August 2011

Mock objects are designed to stand in.. allow us to test code without having to worry about consequences

  • Dummy – return a predefined response, can’t vary response based on input parameters
  • Fake
  • Stub
  • Mock

Dummy

Code Snippet
class DummyDependency : IDependency {
        public int GetValue(string s) {
            return 1;
        }
    }

    [TestFixture]
    public class InitialTests {

        [Test]
        public void TestWithADummy() {
            var dependency = new DummyDependency();
            var dependentClass = new DependentClass(dependency);
            const string param = "abc";
            const int expectedResultOne = 1;

            var resultOne = dependentClass.GetValue(param);
            Assert.AreEqual(expectedResultOne, resultOne);
        }
    }

constructor based dependency injection:

Code Snippet
    public interface IDependency {
        int GetValue(string s);
    }

    public class DependentClass : IDependency {
        private readonly IDependency _dependency;

        public DependentClass(IDependency dependency) {
            _dependency = dependency;
        }

        public int GetValue(string s) {
            return _dependency.GetValue(s);
        }
    }

Fakes and Stubs

Code Snippet
[Test]
        // a fake is a similar thing
        public void TestWithAStub() {
            var dependency = new StubDependency();
            var dependentClass = new DependentClass(dependency);
            const string param1 = "abc";
            const string param2 = "xyz";

            const int expectedResultOne = 1;
            const int expectedResultTwo = 2;

            var resultOne = dependentClass.GetValue(param1);
            var resultTwo = dependentClass.GetValue(param2);

            Assert.AreEqual(expectedResultOne, resultOne);
            Assert.AreEqual(expectedResultTwo, resultTwo);
        }

 

Code Snippet
class StubDependency : IDependency {
        public int GetValue(string s) {
            if (s == "abc")
                return 1;
            if (s == "xyz")
                return 2;
            return 0;
        }
    }

Mocks

More complex and can contain state.

Moq

Testing a concrete implementation here, which will take 30s!

Code Snippet
    public interface ILongRunningLibrary {
        string RunForALongTime(int interval);
    }

    public class LongRunningLibrary : ILongRunningLibrary {
        public string RunForALongTime(int interval) {
            var timeToWait = interval * 1000;
            Thread.Sleep(timeToWait);
            return string.Format("Waited {0} seconds", interval);
        }
    }

    [TestFixture]
    public class MoqExamples {
        private ILongRunningLibrary _longRunningLibrary;
        [SetUp]
        public void SetupForTest() {
            _longRunningLibrary = new LongRunningLibrary();
        }

        [Test]
        public void TestLongRunningLibrary() {
            const int interval = 30;
            var result = _longRunningLibrary.RunForALongTime(interval);
            Debug.WriteLine("Return from method was a '{0}'", result);
        }
    }

And now using Moq:

Code Snippet
public interface ILongRunningLibrary {
        string RunForALongTime(int interval);
    }

    public class LongRunningLibrary : ILongRunningLibrary {
        public string RunForALongTime(int interval) {
            var timeToWait = interval * 1000;
            Thread.Sleep(timeToWait);
            return string.Format("Waited {0} seconds", interval);
        }
    }

    [TestFixture]
    public class MoqExamples {
        private Mock<ILongRunningLibrary> _longRunningLibrary;
        [SetUp]
        public void SetupForTest() {
            _longRunningLibrary = new Mock<ILongRunningLibrary>();
        }

        [Test]
        public void TestLongRunningLibrary() {
            const int interval = 30;
            //_longRunningLibrary.Setup(lrl => lrl.RunForALongTime(30))
            //.Returns("This method has been mocked!");
            _longRunningLibrary.Setup(lrl => lrl.RunForALongTime(It.IsAny<int>()))
                                .Returns((int s) =>
                                    string.Format(
                                        "This method has been mocked!" +
                                        "The input value was {0}", s));
            _longRunningLibrary
                            .Setup(lrl => lrl.RunForALongTime(0))
                            .Throws(new ArgumentException("0 is not a valid interval"));

            var result = _longRunningLibrary.Object.RunForALongTime(interval);
            Debug.WriteLine(result);
        }

        [Test]
        public void TestLongRunningLibraryFailsWhenZeroIsPassedIn() {
            const int interval = 0;
            //_longRunningLibrary.Setup(lrl => lrl.RunForALongTime(30))
                                //.Returns("This method has been mocked!");
            _longRunningLibrary.Setup(lrl => lrl.RunForALongTime(It.IsAny<int>()))
                                .Returns((int s) =>
                                    string.Format(
                                        "This method has been mocked!" +
                                        "The input value was {0}", s));
            _longRunningLibrary
                            .Setup(lrl => lrl.RunForALongTime(0))
                            .Throws(new ArgumentException("0 is not a valid interval"));
                                
            var result = _longRunningLibrary.Object.RunForALongTime(interval);
            Debug.WriteLine(result);
        }
    }
| | # 
# Monday, 05 July 2010
( ASP.NET MVC | Moq )

While using Moq to test an MVC app, and Scott Hanselman’s NerdDinner app, I kept getting these strange errors.

Problem was permissions on:

C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys

In Windows XP Pro, you need to disable simple file sharing.. from http://www.windowsbbs.com/windows-xp/52502-cannot-change-permissions-folder.html

image

Then I set perms to be full control to everyone:

image

More granular perms are in here: http://groups.google.co.uk/group/RhinoMocks/browse_thread/thread/26df68ff01567509/5ddebf407228edc4

Now this code works:

image

On Win7: C:\Users\All Users\Microsoft\Crypto\RSA\MachineKeys

Takes some messing around with granting ownership of the directory, then setting full control to everyone.

| | #