Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Wednesday, September 14, 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);
        }
    }
Comments [0] | | # 
# Tuesday, August 16, 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!).

Comments [0] | | # 

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);
        }
    }
Comments [0] | | # 
# Friday, August 12, 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);
        }
    }
Comments [0] | | # 
# Tuesday, July 06, 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.

Comments [0] | | #