Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Sunday, 22 December 2013
( TDD )

Using TestComplete – expensive!  Project in e:\Dev\BookStore

  • Genre
  • Book
  • Author

BookStoreManagerController – for adding and removing books

public class BookStoreDbInitializer : DropCreateDatabaseAlways<BookStoreContext>
{
    protected override void Seed(BookStoreContext context)
    {
        context.Authors.Add(new Author { Name = "Jesse Liberty" });
        context.Authors.Add(new Author { Name = "Dickens" });
        context.Authors.Add(new Author { Name = "Melville" });
        context.Authors.Add(new Author { Name = "Stephenson" });
        context.Genres.Add(new Genre { Name = "Programming" });
        context.Genres.Add(new Genre { Name = "Non-Fiction" });

        context.Books.Add(new Book
        {
            Author = new Author { Name = "Proust" },
            Genre = new Genre { Name = "Fiction" },
            Price = 19.95m,
            Title = "In Search of Lost Time"
        });


        base.Seed(context);
    }
}

Create a new database everytime we start the project!  Using EF4.1

image

image
Interesting dropdowns.

Testing is just like Selenium.

image

| | # 
# Saturday, 21 December 2013
( MVC | TDD )

Jesse Liberty PS video

  • Test only the code you wrote
    • do not test with other modules (that is integration testing)
  • Business logic should not be in the controller
    • should be in the Model or Repository layer
  • Mocking – allows us to simulate a DB
    • Stubs.. very simple.. return a single value
    • Fakes.. a bit of logic
    • Full Mocks
  • DI

Mocking

Remember you are are not testing the mocked object… but you are testing your other code

  • Interfaces
  • Methods
  • Properties
  • LINQ Queries
  • Non-public members and types

DI allows us to insert our mock

  • Constructor injection (poor mans)
  • Property injection (feed mock to specific property)

Part 2

image

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }
}

He’s called his interface Repository (Uncle Bob recommendation no I)

public interface Repository
{
    List<Product> GetAll();
}
[TestMethod]
public void Index_Returns_All_Products_in_DB()
{
    var productRepository = Mock.Create<Repository>();
    // Accepts a lambda statement
    Mock.Arrange(() => productRepository.GetAll())
        .Returns(new List<Product>()
        {
            new Product {Genre = "Fiction", ID=1, Name="Moby Dick", Price=12.50m},
            new Product {Genre = "Fiction", ID=2, Name="War and Peace", Price=17m}
        })
        .MustBeCalled();

    // Act
    HomeController controller = new HomeController(productRepository);
    ViewResult viewResult = controller.Index();
    var model = viewResult.Model as IEnumerable<Product>;

    // Assert
    Assert.AreEqual(2, model.Count());
}

Using JustMock to mock out the Interface’d Repository.

Part 3

Determine and explore requirements throughout this process

[TestMethod]
public void FindByGenreReturnsAllInGentre()
{
    // Arrange
    // Returning a mock - concrete implementation of the Repository interface
    Repository productRepository = Mock.Create<Repository>();
    Mock.Arrange(() => productRepository.GetAll())
        .Returns(new List<Product>()
        {
            new Product {Genre = "Fiction", ID=1, Name="Moby Dick", Price=12.50m},
            new Product {Genre = "Fiction", ID=2, Name="War and Peace", Price=17m},
            new Product {Genre = "Non-Fiction", ID=3, Name="Chemistry", Price=20m}
        })
        .MustBeCalled();

    // Act
    var controller = new HomeController(productRepository);
    ViewResult viewResult = controller.FindByGenre("Fiction");
    var model = viewResult.Model as IEnumerable<Product>;

    // Assert
    Assert.AreEqual(2, model.Count());
    Assert.AreEqual("Moby Dick", model.ToList()[0].Name);
    Assert.AreEqual("War and Peace", model.ToList()[1].Name);
}

image

| | # 
( MVC | TDD )

http://pluralsight.com/training/Courses/TableOfContents/aspdotnet-mvc-testing-from-scratch
Jesse Liberty

FizzBuzzMVC

Mocking: JustMock (free)

Unit Testing

  • Test a single unit – a class or (better) a method
  • Test in isolation

SOLID Principles and Refactoring

Uncle Bob

  • Single Responsibility
    • Exactly one reason for that method / class to change
    • One responsibility per method / class
  • Open-Closed Principle
    • Open for extension, closed for modification.. so dependable API
    • Derive but don’t modify internals
  • Liskov Substitution
    • Ability to replace instances with their subtypes
  • Interface Segregation Principle
    • No client should be forced to depend on methods it doesn’t use
    • Small Interfaces
  • Dependency Inversion
    • Depend on abstractions (ie DI – just like in controller which we implement poor mans DI)

Names matter

  • Classes = Nouns (things)
  • Methods = Verbs (doing)
  • Collections are plural
  • Hungarian eg lblName… avoid.

.

  • Functions should be small
  • Do one thing
  • Avoid switch
    • Use derived types
  • Minimize parameters into a method
    • Three or more increases cognitive load

DRY – Don’t Repeat Yourself

YAGNI – You Aint Goona Need It

Use fewer and smarter comments – use to explain intent

TODO Comments – use

FizzBuzz

image

image

[TestMethod]
public void Given1Return1()
{
    var controller = new FizzBuzzController();

    var result = controller.Index(1) as ViewResult;

    string expected = "1";
    string actual = result.ViewBag.Output;

    Assert.AreEqual(expected, actual);
}
public ActionResult Index(int value)
{
    for (int i = 1; i <= value; i++)
    {
        if (i % 3 == 0)
        {
            ViewBag.Output += "Fizz ";
        }
        else
        {
            ViewBag.Output += i.ToString() + " ";
        }    
    }
    return View();
}
        [TestMethod]
        public void Given1Return1()
        {
            var controller = new FizzBuzzController();
            var result = controller.Index(1) as ViewResult;
            string expected = "1 ";
            string actual = result.ViewBag.Output;
            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        public void Given3Return12Fizz()
        {
            var controller = new FizzBuzzController();
            var result = controller.Index(3) as ViewResult;
            string expected = "1 2 Fizz ";
            string actual = result.ViewBag.Output;
            Assert.AreEqual(expected, actual);
        }

Red Green refactor.

public ActionResult Index(int value)
{
    for (int i = 1; i <= value; i++)
    {
        if (i % 3 == 0 && i % 5 == 0)
        {
            ViewBag.Output += "FizzBuzz ";
        }
        else if (i % 3 == 0)
        {
            ViewBag.Output += "Fizz ";
        }
        else if (i % 5 == 0)
        {
            ViewBag.Output += "Buzz ";
        }
        else
        {
            ViewBag.Output += i.ToString() + " ";
        }    
    }
    return View();
}

Put in a view @ViewBag.Ouput  then /FizzBuzz/Index?value=15

| | # 
# Monday, 16 December 2013
( MVC4 | TDD )

Unit test controllers…isolate from Web, Mail, Database?

  • Could write Unit Tests that hit the db
    • downside is slower
    • downside is setup too
      • SQL Server compact is better
      • Transactions are good
  • Isolate

Extract an interface on DbContext

public interface IApplicationDbContext : IDisposable
{
    IQueryable<T> Query<T>() where T : class;
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IApplicationDbContext
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {}

    public DbSet<Restaurant> Restaurants { get; set; }
    public DbSet<RestaurantReview> Reviews { get; set; }

    IQueryable<T> IApplicationDbContext.Query<T>()
    {
        // If someone wants a Query<Restaurant> then this Set will work
        return Set<T>();
    }

    void IDisposable.Dispose()
    { }
}

Extracting a simple interface of Query<T>

public class HomeController : Controller
{
    IApplicationDbContext _db;

    public HomeController()
    {
        _db = new ApplicationDbContext();
    }

    public HomeController(IApplicationDbContext db)
    {
        _db = db;
    }

    public ActionResult Index(string searchTerm = null, int page = 1)
    {
        var model = _db.Query<Restaurant>()
                        //.Restaurants
                        .OrderByDescending(r => r.Reviews.Average(review => review.Rating))
                        .Where(r => searchTerm == null || r.Name.StartsWith(searchTerm))
                        .Select(r => new RestaurantListViewModel
                                {
                                    Id = r.Id,
                                    Name = r.Name,
                                    City = r.City,
                                    Country = r.Country,
                                    NumberOfReviews = r.Reviews.Count()
                                })
                            .ToPagedList(page, 10);
        return View(model);
    }

Simple DI, and adding in Query

[TestMethod]
public void Index()
{
    // Arrange
    var db = new FakeIApplicationDbContext();
    db.AddSet(TestData.Restaurants);
    HomeController controller = new HomeController(db);

    // Act
    ViewResult result = controller.Index() as ViewResult;

    // Assert
    Assert.IsNotNull(result);
}

Passing in a Fake DbContext

namespace OdeToFood.Tests
{
    public class FakeIApplicationDbContext : IApplicationDbContext
    {
        public Dictionary<Type, object> Sets = new Dictionary<Type, object>();

        public IQueryable<T> Query<T>() where T : class
        {
            return Sets[typeof(T)] as IQueryable<T>;
        }

        public void AddSet<T>(IQueryable<T> objects)
        {
            Sets.Add(typeof(T), objects);
        }

        void IDisposable.Dispose() { }
    }
}

Generics laden!

And now a simple TestData generator

public class TestData
{
    public static IQueryable<Restaurant> Restaurants
    {
        get
        {
            var restaurants = new List<Restaurant>();
            for (int i = 0; i < 100; i++)
            {
                var restaurant = new Restaurant();
                restaurant.Reviews = new List<RestaurantReview>()
                {
                    new RestaurantReview {Rating=4}
                };
                restaurants.Add(restaurant);
            }
            return restaurants.AsQueryable();
        }
    }
}

Faking out HttpContext

eg if have an If(ajaxrequest)

    public class FakeIApplicationDbContext : IApplicationDbContext
    {
        public Dictionary<Type, object> Sets = new Dictionary<Type, object>();

        public IQueryable<T> Query<T>() where T : class
        {
            return Sets[typeof(T)] as IQueryable<T>;
        }

        public void AddSet<T>(IQueryable<T> objects)
        {
            Sets.Add(typeof(T), objects);
        }

        void IDisposable.Dispose() { }
    }

        [TestMethod]
        public void Index()
        {
            // Arrange
            var db = new FakeIApplicationDbContext();
            db.AddSet(TestData.Restaurants);
            HomeController controller = new HomeController(db);
            // Was needed for a call in HomeController for isAjaxRequest.
            controller.ControllerContext = new FakeControllerContext();

sdf

[TestMethod]
public void Index()
{
    // Arrange
    var db = new FakeIApplicationDbContext();
    db.AddSet(TestData.Restaurants);
    HomeController controller = new HomeController(db);
    // Was needed for a call in HomeController for isAjaxRequest.
    controller.ControllerContext = new FakeControllerContext();

    // Act
    ViewResult result = controller.Index() as ViewResult;
    IEnumerable<RestaurantListViewModel> model = result.Model as IEnumerable<RestaurantListViewModel>;

    // Assert
    Assert.AreEqual(10, model.Count());
}

Very useful pulling out the 10 that should be there… as the home controller passes in Take(10) of the 100 results that are in the test data.

| | # 
( Dojo | TDD )

http://pluralsight.com/training/Courses/TableOfContents/the-coding-dojo

  • Introduction and retrospective
  • Write tests as well as code
  • Show your working
  • Moderator and facilitator

Fun way to learn!

Practical Coding Skills

  • IDE Keyboard shortcuts
  • Pair programming
  • TDD
  • Refactoring
  • Designing good test cases
  • Working incrementally, and committing code often
  • SOLID
  • OO
  • Functional

http://pluralsight.com/training/Courses/TableOfContents/test-first-development-1
http://pluralsight.com/training/Courses/TableOfContents/test-first-development-2

Deliberate practice.. on work stuff not as good as harder to make mistakes (riskier)

  • Trying to do something you can’t comfortably do
  • Breaking down a skills into components you can practice separately

Forming good habits

Code Katas

http://codekata.pragprog.com/2007/01/code_kata_backg.html#more

Small exercises that you repeat

Leap Years

Write a function that returns true or false depending on whether its input integer is a leap year or not

A leap year is divisible by 4, but is not otherwise divisible by 100, unless it is also divisible by 400

eg 1996-t, 2001-f, 2000-t, 1900-f

 

eg Leap Years. eg 1996 –> true, 2001 –>false, 2000 –> true

| | # 
# Wednesday, 13 March 2013
( BDD | TDD )

A great presentation by Seb with 25 years of experience.  If I could remember one thing (paraphrased)

“Testing code is every bit as important as production code, as it communicates how the system behaves”

http://scottishdevelopers.com/ hosted Seb Rose talking about testing.  http://claysnow.co.uk/ and info http://claysnow.co.uk/pillars-to-principles and http://www.slideshare.net/sebrose/bad-test-good-test

Unit tests should be:

  • Understandable
    • Unit tests are the documentation of what the system does
    • Every bit as important as production code
  • Maintainable
    • DRY in tests
    • Builder pattern to help eg StandardTestUser().Smoker().Age(35).Build()
  • Repetable
    • Should be the same each time (ie no dates, random numbers)
  • Necessary
    • ie not triangulation tests – so all the small tests we use in TDD to drive out code
  • Granular
    • a single behaviour
  • Fast
    • <1 minute for sure.. < 10 secs ideally.  Partition tests

Testing pyramid – see Steve Freemans book.  UI/System tests 5%, Integration 20%, Unit 75%

Books

http://www.amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/0321503627/ref=la_B002O21DQW_1_1?ie=UTF8&qid=1363168437&sr=1-1 – Steve Freeman

Working Effectively with Legacy Code – Code that has no tests

http://www.amazon.co.uk/Things-Every-Programmer-Should-Know/dp/0596809484

https://leanpub.com/codingdojohandbook

Deliberate Learning

Doing small problems multiple times to practice:

image
http://www.cyber-dojo.com/

| | # 
# Friday, 30 December 2011
( Patterns | TDD | TekPub )

“An event in C# is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object.”

eg every time we call Add, we want a notification to clients (logger) that something interesting happened.

  • publisher
  • subscriber

Action<string>

- if we were in .NET2 then public event would have to be of some delegate type.

-  Would have to declare a delegate type of function that would return a string

3.5  Action<T>

public class StringCalculator
    {
        //declare and assign it at the same time (so don't have to check it is null every time)
        public event Action<string> OnAdd = delegate { };

        private readonly ILogger logger;
        public ILogger SecondaryLogger { get; set; }

        public StringCalculator(ILogger logger)
        {
            this.logger = logger;
        }

        public int Add(string numbers)
        {
            OnAdd.Invoke(numbers);
            LogNumbers(numbers);
            if (IsEmpty(numbers))
            {
                return DefaultEmptyResult();
            }
            return AllOtherValues(numbers);
        }

declaring the event, and invoking when add is pressed:

[Test]
        public void Add_Always_TriggersOnAddEvent()
        {
            //bool wasTriggered = false;
            string eventParam = null;

            StringCalculator sc = GetDefaultCalc();
            //lambda way
            //sc.OnAdd += s => wasTriggered = true;

            //so whenever someone invokes the event, wasTriggered will turn to true
            //sc.OnAdd += delegate(string s) { wasTriggered = true; };
            
            //register for event
            sc.OnAdd += delegate(string s) { eventParam = s; };

            sc.Add("1");

            //Assert.IsTrue(wasTriggered);
            Assert.AreEqual("1", eventParam);
        }

subscribing to event, passing in a delegate

Extract and Override

very powerful as can break dependencies easily without interfaces

| | # 
# Wednesday, 28 December 2011
( TDD | TekPub )

Interaction tests.. ie interaction between objects..usually have to replace one of the objects

source control:

  • git init,
  • git add .
  • git commit –am “initial load”

going back to simple stuff:

If we have a Fake object and we do Asserts against it, it is a Mock object:

//interaction tests!
        [Test]
        public void Add_EmptyString_CallsTheLogger()
        {
            FakeLogger fakeLog = new FakeLogger();
            StringCalculator sc = new StringCalculator(fakeLog);

            sc.Add("");

            StringAssert.Contains("empty", fakeLog.WrittenWith);
        }

poor mans injecting into the constructor a logger

    public class StringCalculator
    {
        private readonly ILogger logger;

        public StringCalculator(ILogger logger)
        {
            this.logger = logger;
        }

        public int Add(string numbers)
        {
            LogNumbers(numbers);
            if (IsEmpty(numbers))
            {
                return DefaultEmptyResult();
            }
            return AllOtherValues(numbers);
        }

        private void LogNumbers(string numbers)
        {
            if (IsEmpty(numbers))
                logger.Write("empty");
            else
                logger.Write(numbers);
        }

        private bool IsEmpty(string numbers)
        {
            return string.IsNullOrEmpty(numbers);
        }

        private static int AllOtherValues(string numbers)
        {
            return int.Parse(numbers);
        }

        private static int DefaultEmptyResult()
        {
            return 0;
        }

 

Call a WebService if the log is full

[TestCase("1")]
        [TestCase("2")]
        [TestCase("333")]
        public void Logging_SingleNumberAndFirstLoggerIsFull_SecondaryLoggerIsCalled(string number)
        {
            FakeLogger firstLogger = new FakeLogger();
            FakeLogger secondLogger = new FakeLogger();

            firstLogger.WillThrow = new Exception("out of space");
            StringCalculator sc = new StringCalculator(firstLogger);
            sc.SecondaryLogger = secondLogger;

            sc.Add(number);

            //secondaryLogger is the mock object
            StringAssert.Contains(number, secondLogger.WrittenWith);
        }

the first test, which then became a testCase.

        public int Add(string numbers)
        {
            LogNumbers(numbers);
            if (IsEmpty(numbers))
            {
                return DefaultEmptyResult();
            }
            return AllOtherValues(numbers);
        }

        private void LogNumbers(string numbers)
        {
            try
            {
                if (IsEmpty(numbers))
                    logger.Write("empty");
                else
                    logger.Write(numbers);
            }
            catch (Exception e)
            {
                SecondaryLogger.Write(numbers);
            }
        }

nice having logic at same level of abstraction.

| | # 
# Tuesday, 27 December 2011
( kata | TDD | TekPub )

Code Kata are exercises that you can do

osherove.com/kata

  • 1 method, 1 parameter
  • 0 or more numbers
  • seperated by a comma

eg var sum = Calculator.Sum(1,3,10);

Alt N for PackageManagerConsole.

Start with simplest test..empty string.

[TestFixture]
    public class StringCalculatorTests
    {
        [Test]
        public void Add_EmptyString_ReturnsZero()
        {
            //start with this even though StringCalculator doesn't exist yet
            StringCalculator sc = new StringCalculator();

            //method doesn't exist
            int result = sc.Add("");

            //first kind of syntax
            //Assert.AreEqual(0, result);
            //second kind of syntax
            Assert.That(result, Is.EqualTo(0));
        }

        [Test]
        public void Add_SingleNumber_ReturnsThatSingleNumber()
        {
            StringCalculator sc = new StringCalculator();

            int result = sc.Add("1");

            Assert.AreEqual(1, result);
        }
    }

    public class StringCalculator
    {
        public int Add(string numbers)
        {
            return 0;
        }
    }

just about to get the 2nd test passing… notice no refactoring yet as only do that when 2 tests are actually passing!

Just assigned Alt Y to rerun all tests so can keep the cursor in the add method.

Ctrl Alt P – assign parameter

String Calc – first bits

nCover

image_thumb1

100% code coverage at the moment.

[TestCase("1,2",3)]
[TestCase("1,3",4)]
[TestCase("4,5",9)]
[TestCase("14,44",58)]
[TestCase("1,2,3", 6)]
[TestCase("1,2,3,4,5", 15)]
public void Add_MultipleNumbers_ReturnsTheSum(string numbers, int expected)
{
StringCalculator sc = MakeCalc();

int result = sc.Add(numbers);

Assert.AreEqual(expected, result);
}

private static StringCalculator MakeCalc()
{
return new StringCalculator();
}
}

public class StringCalculator
{
public int Add(string numbers)
{
if (numbers.Length == 0)
return 0;
if (numbers.Length == 1)
return int.Parse(numbers);

var listOfNumbers = numbers.Split(',');
int sumOfNumbers = 0;
foreach (var number in listOfNumbers)
sumOfNumbers += int.Parse(number);

return sumOfNumbers;

}
}

first shot at getting multiple numbers being summed together

[TestFixture]
    public class StringCalculatorTests
    {
        private static StringCalculator MakeCalc()
        {
            return new StringCalculator();
        }

        [Test]
        public void Add_EmptyString_ReturnsZero()
        {
            //start with this even though StringCalculator doesn't exist yet
            StringCalculator sc = MakeCalc();

            //method doesn't exist
            int result = sc.Add("");

            //first kind of syntax
            //Assert.AreEqual(0, result);
            //second kind of syntax
            Assert.That(result, Is.EqualTo(0));
        }

        [TestCase("1",1)]
        [TestCase("2", 2)]
        public void Add_SingleNumber_ReturnsThatSingleNumber(string numbers, int expected)
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add(numbers);

            Assert.AreEqual(expected, result);
        }

        [TestCase("1,2",3)]
        [TestCase("1,3",4)]
        [TestCase("4,5",9)]
        [TestCase("14,44",58)]
        [TestCase("1,2,3", 6)]
        [TestCase("1,2,3,4,5", 15)]
        public void Add_MultipleNumbers_ReturnsTheSum(string numbers, int expected)
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add(numbers);

            Assert.AreEqual(expected, result);
        }

        [Test]
        public void Add_TwoNumbersWithLineFeedDelimeter_ReturnsTheSum()
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add("1\n2");

            Assert.AreEqual(3, result);
        }

        [TestCase("1\n3", 4)]
        [TestCase("1\n2\n3", 6)]
        [TestCase("1\n222\n3\n20", 246)]
        public void Add_MultipleNumbersWithLineFeedDelimeter_ReturnsTheSum(string numbers, int expected)
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add(numbers);

            Assert.AreEqual(expected, result);
        }

        [Test]
        public void Add_TwoNumbersWithGeneralDelimeter_ReturnsTheSum()
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add("//;\n1;2");

            Assert.AreEqual(3, result);
        }

        [TestCase("//;\n1;2", 3)]
        [TestCase("//;\n1;2;3", 6)]
        [TestCase("//;\n1;2;3;20", 26)]
        [TestCase("//x\n1x2x3x20", 26)]
        public void Add_MultipleNumbersWithGeneralDelimeter_ReturnsTheSum(string numbers, int expected)
        {
            StringCalculator sc = MakeCalc();

            int result = sc.Add(numbers);

            Assert.AreEqual(expected, result);
        }

        [Test]
        public void Add_NegativeNumber_ThrowsCorrectExceptionWithNegativeNumber()
        {
            StringCalculator sc = MakeCalc();

            Exception exception = Assert.Throws<Exception>(delegate
                                                                {
                                                                    sc.Add("-1");
                                                                });
            Assert.AreEqual("Negatives not allowed: -1", exception.Message);
        }

        [Test]
        public void Add_MultipleNegativeNumbers_ThrowsAnExceptionWithAllNegativeNumbers()
        {
            StringCalculator sc = MakeCalc();

            Exception exception = Assert.Throws<Exception>(delegate
                                                                {
                                                                    sc.Add("-1,-2");
                                                                });
            
            Assert.AreEqual("Negatives not allowed: -1, -2", exception.Message);
        }

        [TestCase("-1,-2", "-1, -2")]
        [TestCase("-1,-2,-3", "-1, -2, -3")]
        [TestCase("-1,2,-3", "-1, -3")]
        public void Add_MixedMultipleNegativeAndPositiveNumbers_ThrowsAnExceptionWithAllNegativeNumbers(string numbers, string expected)
        {
            StringCalculator sc = MakeCalc();

            Exception exception = Assert.Throws<Exception>(delegate
                                                                {
                                                                    sc.Add(numbers);
                                                                });

            Assert.AreEqual("Negatives not allowed: " + expected, exception.Message);
        }
    }

    public class StringCalculator
    {
        public int Add(string numbers)
        {
            string delimeter;
            if (numbers.Contains("//"))
            {
                delimeter = numbers.Substring(2, 1);
                numbers = numbers.Substring(4, numbers.Length-4);
                numbers = numbers.Replace(delimeter, ",");
            }
            else if (numbers.Contains("\n"))
                numbers = numbers.Replace("\n", ",");

            if (numbers.Length == 0)
                return 0;
            if (numbers.Length == 1)
                return int.Parse(numbers);

            if (numbers.Length == 2)
            {
                int i = int.Parse(numbers);
                if (i < 0)
                    throw new Exception("Negatives not allowed: " + i.ToString());
                return i;
            }

            string[] listOfNumbers = numbers.Split(',');

            string errorMesssage = "Negatives not allowed: ";
            bool showErrorMessage = false;
            foreach (var number in listOfNumbers)
            {
                int i = int.Parse(number);
                if (i < 0)
                {
                    errorMesssage += i.ToString() + ", ";
                    showErrorMessage = true;
                }
            }

            //get rid of final comma and space
            if (showErrorMessage)
            {
                errorMesssage = errorMesssage.Substring(0, errorMesssage.Length - 2);
                throw new Exception(errorMesssage);
            }

            int sumOfNumbers = 0;
            foreach (var number in listOfNumbers)
                sumOfNumbers += int.Parse(number);

            return sumOfNumbers;

        }
    }

first show at just getting tests to pass.

Started refactorugb with help from videos on: http://osherove.com/tdd-kata-1/

linq, extension methods, make more readable using methods

http://vimeo.com/27977192

| | # 
( TDD | TekPub )

From tekpub.com

Unit Tests – “.. a test of a small functional piece of code”

run in memory (not db!).. all under our control…always expect unit tests to pass

ie not integration test.. which has dependencies.

 

Creating a new blank solution (actually called it MyProduct)

image

then create a class library called MyProduct.Logic

image

downloaded Resharper.

Alt Enter to get rid of usings

 

image

setting up a separate project for UnitTests (and further down the line, integration tests).

image

installing unit via nuget console window. (Alt t n o)

Don’t need punit or the mock dlls.  So just nunit.framework.

 

tests – don’t test math forumulae.  Just logic.

Test Naming

whats being tested:  Add

scenario:  TwoNumbers

expected behaviour

eg Add_TwoNumbers_ReturnsTheSum

Test Lint – tells you if no asserts.  Logic in tests eg 1+2   Assert.AreEqual(1+2, result)

 

TestDriven.NET

Tools Options Keyboard

he uses alt R to rerun tests ( I use Alt T, and Alt D for debug)

Refactoring

refactor out constructor stuff when > 2 tests

put initialisation in setup – but this can be confusing when looking at a test way down the page.. so refactor to factory

[TestFixture]
    public class MyCalculatorTests
    {
        [Test]
        public void Add_TwoNumbers_ReturnsTheSum()
        {
            MyCalculator c = GetNewCalc();

            int result = c.Add(1, 2);

            Assert.AreEqual(3, result);
        }

        private static MyCalculator GetNewCalc()
        {
            return new MyCalculator();
        }
    }

static factory.

image

but attribute hmmm.. GetNewCalc could throw and exception.

Use Assert.Throws.

Delegates and Lambda expressions

[Test]
        //[ExpectedException(typeof(Exception))]
        public void Add_FirstParamNegative_Throw()
        {
            MyCalculator c = GetNewCalc();

            int ANY_POSITIVE = 1;

            //c.Add(-1, ANY_POSITIVE);

            //parameter is a delegate to a method here
            //but would have same problem as putting in an attribute
            //ie wouldn't know that GetNewCalc didn't fire an exception
            //any line of Code could throw an exception
            //Assert.Throws<Exception>(Code);

            //anonymous delegate or anonymous method
            //even though its in the scope on an anon method
            //it knows about c
            Assert.Throws<Exception>(delegate
                                         {
                                             c.Add(-1, ANY_POSITIVE);
                                         });

            //lambda expression - shorter delegate notation
            //method doesn't have any name.
            //after => body of method only has one line
            //Assert.Throws<Exception>(() => c.Add(-1, ANY_POSITIVE));
        }
  private void Code()
        {
            //c.Add(-1, ANY_POSITIVE);
        }

he prefers the delegate way – things more  readable than lambda

TDD and Learning new things

  • Copy
  • Why
  • Improvise..eg make own naming convention

Start with a failing test

Make test pass – make code as dumb and simple as possible.  Not generic.  Better tests.. better code coverage…how would 11 year old solve it?

Refactor – changing existing code without changing functionality

So, writing the test first for second param being negative throwing:

 

New Feature – Calculator Remembers Last Result

Doing the test first.. so can see that the field / property  c.LastResult doesn’t exist yet!

[Test]
        public void ResultMemory_CallingAdd_ResultIsSavedForLater()
        {
            MyCalculator c = GetNewCalc();
            int result = c.AddPositives(1, 2);
            Assert.AreEqual(3, c.LastResult);
        }

easiest way to make the test pass:

        //a backing field
        private int _lastResult;
        //property (not an auto property)
        public int LastResult
        {
            get { return 3; }
            set { _lastResult = value; }
        }

TestCase – parameterised

[TestCase(1,2,3)]
        public void ResultMemory_CallingAdd_ResultIsSavedForLater(int a, int b, int expected)
        {
            MyCalculator c = GetNewCalc();
            int result = c.AddPositives(a, b);
            Assert.AreEqual(expected, c.LastResult);
        }
| | # 
# Tuesday, 13 September 2011
( SpecFlow | TDD )

 

image

goes to ShoppingCart/Add post method then redirectToAction Index

image

checkout:

image

asdf

image

asdf

image

image

| | # 
( TDD )

Code Snippet
namespace BookShop.Services.UnitTests.CatalogServicesTests
{
    [TestClass]
    public class GetOfferedBooksTests
    {
        private Mock<IDomainContext> domainContextFake;
        private CatalogServices service;

        [TestInitialize]
        public void Setup()
        {
            domainContextFake = new Mock<IDomainContext>();
            service = new CatalogServices(domainContextFake.Object);
        }

        [TestMethod]
        public void WhenCatalogIsEmpty_ShouldReturnEmptyList()
        {
            // given
            domainContextFake.SetupGet(m => m.BookRepository).Returns(new StubRepository<Book>());

            // when
            var result = service.GetOfferedBooks();

            // then
            Assert.AreEqual(0, result.Count);
        }

        [TestMethod]
        public void WhenCatalogHasMaximumThanThreeBooks_ShouldReturnAllOfThem()
        {
            // given
            var book1 = new Book { Price = 1m };
            var book2 = new Book { Price = 2m };

            domainContextFake.SetupGet(m => m.BookRepository).Returns(new StubRepository<Book>(book1, book2));

            // when
            var result = service.GetOfferedBooks();

            // then
            CollectionAssert.AreEquivalent(new[] { book1, book2 }, result);
        }

        [TestMethod]
        public void WhenCatalogHasMoreThanThreeBooks_ShouldReturnTheThreeCheapest()
        {
            // given
            var book1 = new Book { Price = 1m };
            var book2 = new Book { Price = 2m };
            var book3 = new Book { Price = 3m };
            var book4 = new Book { Price = 4m };

            domainContextFake.SetupGet(m => m.BookRepository).Returns(new StubRepository<Book>(book3, book1, book2, book4));

            // when
            var result = service.GetOfferedBooks();

            // then
            CollectionAssert.AreEquivalent(new[] { book1, book2, book3 }, result);
        }
    }

Using Moq to fake out EF DomainContext

IDomainContext

IRepository

Also needs a StubRepository (which sits in UnitTests) and implements IRepository which implements IQueryable

  • Getting interesting as now testing business logic ie if we have 4 books, then must return 3 cheapest
  • Catalog has less than 3 must return all of them
| | # 
# Thursday, 16 December 2010
( Euler | TDD | VS2010 )

I decided to try and do Test Driven Development here, so avoid any small problems.

Problem was that my test harness in VS2010 wouldn’t start the agent: http://stackoverflow.com/questions/4446416/vs2010-test-runner-unable-to-start-agent-process (Edit: It was Avira AntiVirus that was the culprit)

alt text

After reinstalling VS2010, and trying to remember all the changes I’ve made since it last worked.. I can run all the tests, just not in the debugger.  Which is very useful.

Euler problem is interesting:

The sum of the squares of the first ten natural numbers is,

12 + 22 + ... + 102 = 385

The square of the sum of the first ten natural numbers is,

(1 + 2 + ... + 10)2 = 552 = 3025

Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is 3025 − 385 = 2640.

Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.

Source code here:    

 

class Program
{
static void Main(string[] args)
{
stuff thing = new stuff();
double sumOfSqaures = thing.sumOfSquares(100);
double sqaureOfSums = thing.SquareOfSum(100);
double result = sqaureOfSums - sumOfSqaures;
Console.WriteLine(result);
}
}

public class stuff
{
public double sumOfSquares(double upToInt)
{
double total = 0;
for (int i = 1; i <= upToInt; i++)
total += Math.Pow(i, 2);

return total;
}

public double SquareOfSum(int upToInt)
{
double sum = 0;
for (int i = 0; i <= upToInt; i++)
sum += i;

return Math.Pow(sum, 2);
}
}

and test code:

[TestClass]
public class UnitTest1
{
[TestMethod]
public void SumOfSquaresOfFirst10()
{
stuff thing = new stuff();
double result = thing.sumOfSquares(10);
Assert.AreEqual(385, result);
}

[TestMethod]
public void SquareOfSumOfFirst10()
{
stuff thing = new stuff();
double result = thing.SquareOfSum(10);
Assert.AreEqual(3025, result);
}

[TestMethod]
public void DifferenceBetweenTwoAbove()
{
stuff thing = new stuff();
double sumOfSqaures = thing.sumOfSquares(10);
double sqaureOfSums = thing.SquareOfSum(10);
double result = sqaureOfSums - sumOfSqaures;
Assert.AreEqual(2640, result);
}

}

It was great using TDD.. made it very easy, and got answer right first time!

| | #