Search

Categories

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Send mail to the author(s) E-mail

# Friday, 11 September 2015

Below is code from https://github.com/djhmateer/Refactoring,
Based on http://mikehadlow.blogspot.co.uk/2015/08/c-program-entirely-with-static-methods.html  *

The objective of this talk is to refactor a simple Console Application which does an ETL towards code which is:

  • Easier to understand (less WTF’s per minute)
  • Easier to test
  • More pleasing

In general I firstly went in an OO style towards composition.  Then got rid of the ceremony of OO which I didn’t need, and used a simpler functional approach

  • Code looks ‘a little odd to the uninitiated’ *
  • Less lines code
  • Very easy to test
// Functional style of the QuoteImporter app, with logging using a decorator // Firstly decorate each separate function (1,2,3) with it's logger // Then compose together, decorating that top level function too private static void Main() { // 1. ReadFileListOfLines is a function with 0 Parameters, which returns a List of strings // passing it a lambda expression (anonymous function) // Log takes 1 parameter s // ReadFileListOfLines takes 0 parameters // returns a string Func<IEnumerable<string>> readFileListOfLines = () => ReadFileListOfLinesLogger(() => ReadFileListOfLines(), s => Log(s)); // 2. ParseLine takes 1 Parameter string s, which is wired up to lineToParse // using method group - don't need to specify s => Log(s) // returns a Quote Func<string, Quote> parseLine = lineToParse => ParseLineLogger(s => ParseLine(s), Log, lineToParse); // 3. InsertQuoteIntoDatabase takes 1 parameter Quote // returns nothing (therefore its an Action) // quote is wired up to q in the logger Action<Quote> insertQuoteIntoDatabase = quote => InsertQuoteIntoDatabaseLogger(q => InsertQuoteIntoDatabase(q), Log, quote); // Compose the Functions together (without running them) // Passing in a lambda expression (anonymous function) to Action run Action quoteImporter = () => QuoteImporter(readFileListOfLines, parseLine, insertQuoteIntoDatabase); // Decorating logging in the top level function (but no logging in this composition root function) Action run = () => QuoteImporterLogger(quoteImporter,Log); run(); } public static void QuoteImporterLogger(Action quoteImporter, Action<string> log) { log("Start QuoteImporter"); quoteImporter(); log("End QuoteImporter"); } // Top level function, which depends upon 3 other functions (passed in as parameters) public static void QuoteImporter( Func<IEnumerable<string>> readFileListOfLines, Func<string, Quote> parseLine, Action<Quote> insertQuoteIntoDatabase) { IEnumerable<string> lines = readFileListOfLines(); foreach (var line in lines) { var quote = parseLine(line); insertQuoteIntoDatabase(quote); } } // 1 ReadFile List public static IEnumerable<string> ReadFileListOfLinesLogger( Func<IEnumerable<string>> readFileListOfLines, Action<string> log) { log("Start ReadFileListOfLines"); var result = readFileListOfLines(); // Easy way to view contents of IEnumerable to log log($"result of ReadFileListOfLines: {string.Join(Environment.NewLine,result.Select(s => s))}"); log("End ReadFileListOfLines"); return result; } public static IEnumerable<string> ReadFileListOfLines() { string[] fileTextLines = File.ReadAllLines(@"..\..\quotesWithTitles.csv"); return fileTextLines.ToList(); } // 2 ParseLine public static Quote ParseLineLogger( Func<string, Quote> parseLine, Action<string> log, string lineToParse) { log($"Start ParseLine with input {lineToParse}"); Quote result = parseLine(lineToParse); log($"End ParseLine with ouput {result.Title}, {result.Body}"); return result; } public static Quote ParseLine(string line) { string[] values = line.Split(','); if (values.Length < 2) throw new ApplicationException("Unknown state - too few commas"); if (values.Length > 2) throw new ApplicationException("Unknown state - too many commas"); string title = values[0]; string body = values[1]; return new Quote{Title = title,Body = body}; } // 3 InsertQuote public static void InsertQuoteIntoDatabaseLogger( Action<Quote> insertQuoteIntoDatabase,Action<string> log, Quote quote) { log($"Start InsertQuoteIntoDatabase with input {quote.Title} {quote.Body}"); insertQuoteIntoDatabase(quote); log($"End InsertQuoteIntoDatabase"); } private static void InsertQuoteIntoDatabase(Quote quote) { string connectionString = @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Dev\Refactoring\Refactoring\Database1.mdf;Integrated Security=True"; using (var connection = new SqlConnection(connectionString)) { var cmd = new SqlCommand("INSERT INTO Quotes (Title, Text) VALUES (@Title, @Quote)", connection); cmd.Parameters.AddWithValue("@Title", quote.Title); cmd.Parameters.AddWithValue("@Quote", quote.Body); connection.Open(); cmd.ExecuteNonQuery(); } } public static void Log(string message) { Console.WriteLine("log: {0}", message); }

The tests:

[Fact] public void ReadFileList_ShouldReturn3Lines() { IEnumerable<string> result = ImporterFunctional.ReadFileListOfLines(); Assert.Equal(3, result.Count()); } [Fact] public void ParseLine_AValidLine_ShouldReturnAQuote() { string line = "Basic,Programming in Basic causes brain damage - Edsger Wybe Dijkstra"; Quote result = ImporterFunctional.ParseLine(line); Assert.Equal("Basic", result.Title); Assert.Equal("Programming in Basic causes brain damage - Edsger Wybe Dijkstra", result.Body); } [Fact] public void ParseLine_AnEmptyLine_ShouldThrow() { string line = ""; Assert.Throws<ApplicationException>(() => ImporterFunctional.ParseLine(line)); } [Fact] public void ParseLine_TooManyCommas_ShouldThrow() { string line = "asdf,asdf,asdf"; Assert.Throws<ApplicationException>(() => ImporterFunctional.ParseLine(line)); } // 2. Testing the top level Orchestration function [Fact] public void QuoteImporter_GivenMockFile_ShouldInsertIntoMockDatabase() { // 1. ReadFileListOfLines mock string actualLineFromFile = ""; Func<IEnumerable<string>> readFileListOfLines = () => { var listOfLines = new[] {"title, quote here"}; actualLineFromFile = listOfLines[0]; return listOfLines; }; // 2. ParseLine mock string actualLineSentToParseLine = ""; Func<string, Quote> parseLine = s => { actualLineSentToParseLine = s; var quote = new Quote {Title = "title2", Body = "quote here2"}; return quote; }; // 3. InsertQuoteIntoDatabase mock // using a closure IList<Quote> actualQuotes = new List<Quote>(); Action<Quote> insertQuoteIntoDatabase = quote => { actualQuotes.Add(quote); }; // compose. Action is a delegate - doesn't return anything Action run = () => ImporterFunctional.QuoteImporter( readFileListOfLines, parseLine, insertQuoteIntoDatabase); run(); // 1. Testing the ReadFileListOfLines mock was called Assert.Equal("title, quote here", actualLineFromFile); // 2. Testing the mock parser received the data from the ReadFileListOfLines mock Assert.Equal("title, quote here", actualLineSentToParseLine); // 3. Testing the mock database received the data from the ParseLine mock Assert.Equal("title2", actualQuotes[0].Title); Assert.Equal("quote here2", actualQuotes[0].Body); }
| | # 
# Friday, 24 January 2014

There are going to be in depth notes here from this article.  Please consider buying the whole pdf  from him (as I have) as it
http://imar.spaanjaars.com/573/aspnet-n-layered-applications-introduction-part-1

These are purely my notes Imar’s article.

My goals are:

  • Understand this architecture as it “can be used to build real-world, large scale web applications.”
  • Build up from scratch his sample
  • Build out my own Winter Project app with the concepts I like from here
  • Pragmatic
  • Simple

Why Use N-Layers

  • Testability
  • Separation – make it easier to find where things are in large projects eg dataaccess, business logic, UI
  • Reuse – eg a console app can use all the goodness of the main MVC app

image

Previous Versions

  • BLL/DAL – lots of pass through code
  • DAL tight coupling making hard to test
  • SP’s hard to maintain and test
  • Validation was custom – now have better ways

Sidenote: PDF reader being horrible.. trying: http://www.foxitsoftware.com/downloads/

Starting from Scratch

image
Updating all packages

Also have enabled NuGet Package Restore.

image
My DLayer version.

Put into source control – am using github.

| | # 
# Friday, 27 December 2013
( Testing )

 

image
Fast tests and slower tests

image

image

run tests after each build (in VS

Automation

image
Running ClickToBuild.bat. 

  • Firstly builds in debug mode
  • unti tests
  • integration tests
  • release build

ClickToBuildFast.bat

  • debug build
  • unit tests only

image
MSBuild

| | # 
# Thursday, 29 August 2013

http://approvaltests.sourceforge.net/

http://blog.approvaltests.com/

“Approval Tests can be used for verifying objects that require more than a simple assert”

| | # 
# Tuesday, 26 February 2013

This helped hugely when testing over 2 contexts – an Enterprise Library direct call to SP, and Entitiy Framework call using RIA:

Base class for tests:

[TestClass]
    public class TransactedTestBasex
    {
        protected xxxClientAppUserEntitlementReviewEntities xxxEntities;
        protected FakexxxUserProvider fakeUserProvider;
        protected xxxClientAppDomainService xxxDomainService;
        //private TestDbTransaction _transaction;

        private TransactionScope scope;

        [TestInitialize()]
        public void TestStart()
        {
            if (xxxDomainService == null)
            {
                throw new NullReferenceException("Please ensure that you initialise xxxDomainService in the constructor of derived test class.");
            }

            scope = new TransactionScope(TransactionScopeOption.RequiresNew);

            //_transaction = xxxDomainService.BeginTransactionForUnitTest();
        }

        [TestCleanup()]
        public void TestEnd()
        {
            //_transaction.RollbackUnitTest();

            if (scope != null)
            {
                // NOTE: no call to scope.Complete();
                scope.Dispose();
            }
        }
    }

image

MSDTC is unavailable

1) check Distributed Transaction Coordinator in services is running.

| | # 
# Thursday, 06 December 2012

From my question on directly testing RIA Services calls.

http://stackoverflow.com/questions/13510361/ria-services-where-to-call-savechanges-from-a-test/13622463#13622463

http://social.msdn.microsoft.com/Forums/en-US/vstest/thread/5e991b0d-8061-4c4e-a17d-82b4abd58d6c

http://nuget.org/packages/SilverlightToolkit-Testing

Test runners:

Testing front end (eg bindings load)

http://vanderbiest.org/blog/#/Search

How To

Make a new Silverlight standard project.  No RIA link

NuGet search: silverlight testing  find: SilverlightToolkit-Testing

add ref ….libs\RIA Services\v1.0\Libraries\Silverlight\System.ServiceModel.DomainServices.Client

add ref to ClientApp (the SL app we’re testing)

using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using UER.ClientApp.Web.Services;


namespace UER.ClientApp.Tests
{
    [TestClass]
    public class SLTests : SilverlightTest
    {
        [TestMethod]
        public void simpletest_givennothing_1equals1()
        {
            Assert.AreEqual(1, 1);
        }

        [TestMethod]
        [Asynchronous]
        public void AsynchronousWCFCallWithGetStudentWCFCommand()
        {
            var target = new UERDomainContext();

            // Arrange
            EnqueueCallback(() => target.Load(target.GetAccountsQuery()));
            EnqueueConditional(() => !target.IsLoading);
            EnqueueCallback(() => Assert.IsTrue(target.Accounts.Any()));

            // Act
            EnqueueCallback(() => target.Accounts.First().Name = "NewValue");
            EnqueueCallback(() => target.SubmitChanges());

            //// Assert
            EnqueueConditional(() => !target.IsSubmitting);
            EnqueueCallback(() => Assert.IsFalse(target.HasChanges));
            EnqueueTestComplete();
        }
    }
}

Test runner is really not very usable at the top level (ie takes 20s to really run properly?)

image

Try R# and Agunit

Anything to do with databases just seems to saying:  Running… and never finishes.

However AGUnit doesn’t surface async calls currently: http://agunit.codeplex.com/discussions/405630

| | # 
# Monday, 12 November 2012
( mstest | Testing | VS2012 )

Ctrl R T – Run tests in Debug in Current Context

use Debug.WriteLine

Then look in the debug output window

| | # 
# Wednesday, 16 May 2012
( NCrunch | Testing )

http://www.ncrunch.net/download.htm

image

Just sits there and runs any test that I change.

| | # 
# Tuesday, 14 February 2012
( Spider | Testing )

A fun web spider project for CodeCamp.

Am using TDD so first test is:

[Test]
        public void GetHtml_GivenAWebsite_ReturnsTheRawHtml()
        {
            Spider s = new Spider();

            string result = s.GetHtml("http://www.stuff.co.nz");

            Assert.IsNotNullOrEmpty(result);
            Console.WriteLine(result);
        }

and to make it pass:

public string GetHtml(string initialWebsite)
        {
            WebRequest wr = WebRequest.Create(initialWebsite);
            HttpWebResponse response = (HttpWebResponse)wr.GetResponse();
            StreamReader sr = new StreamReader(response.GetResponseStream(),Encoding.UTF8);
            string rawHtml = sr.ReadToEnd();
            return rawHtml;
        }

Find Links

[Test]
        public void GetFirstLink_GivenAWebsite_ReturnsTheFirstLink()
        {
            Spider s = new Spider();

            string result = s.GetFirstLink("http://www.stuff.co.nz");

            Assert.IsNotNullOrEmpty(result);
            Console.WriteLine(result);
        }

and to make it pass:

public string GetFirstLink(string initialWebsite)
        {
            string rawHtml = GetHtml(initialWebsite);

            //look for <a href="
            int x = rawHtml.IndexOf("<a href=");
            
            string y = rawHtml.Substring(x+10);

            int z = y.IndexOf("\"");

            var a = y.Substring(0, z);
            return a;
        }

This does work, however finding multiple links was going to get tedious this way, and regex felt like a much better way to go, so after some googling, and not wishing to use an external library such as http://htmlagilitypack.codeplex.com/

http://stackoverflow.com/questions/122856/parse-html-links-using-c-sharp

Find Unique Links

image

Testing very useful in finding bugs.  Why is it returing holidayhomes.co.nz twice when it should only be visiting unique sites?

code so far is:

public List<String> RunSpider(string startingSite, int numberOfJumps)
        {
            string site = startingSite;
            var listOfSitesVisited = new List<String>();
            for (int i = 1; i <= numberOfJumps; i++)
            {
                Console.WriteLine("Going to: " + site);
                string html = GetHtml(site);
                listOfSitesVisited.Add(site);

                List<String> listOfLinks = GetAllLinks(html);
                List<String> listOfExternalLinks = GetExternalLinks(listOfLinks, site);
                
                string siteToGoToNext = listOfExternalLinks[0].ToString();
                bool keepGoing = true;
                int j = 0;
                while (keepGoing) {
                    if (siteToGoToNext.Contains("holidayhouses.co.nz"))
                    {
                        var c = 1;
                    }

                    if (listOfSitesVisited.Contains(siteToGoToNext))
                    {
                        keepGoing = true;
                        siteToGoToNext = listOfExternalLinks[j].ToString();
                        j++;
                    }
                    else
                    {
                        siteToGoToNext = listOfExternalLinks[j].ToString();
                        keepGoing = false;
                    }
                }

                site = siteToGoToNext;
            }

            return listOfSitesVisited;
        }

and test is:

[Test]
        public void RunSpider_GivenAStartingWebsiteAnd5Jumps_ReturnAListOfWebsitesVisitedWhichShouldBeUnique()
        {
            Spider s = new Spider();
            var listOfSitesVisited = s.RunSpider(startingSite, 5);
            CollectionAssert.Contains(listOfSitesVisited, startingSite);

            CollectionAssert.AllItemsAreUnique(listOfSitesVisited);

            Assert.AreEqual(5, listOfSitesVisited.Count);
        }

debugging to output window:

image

why is siteToGoToNext findsomeone, then it goes to holidayhouses.co.nz?

Solution – a counter was in the wrong place.

Edge Cases

image

image

interesting edge case.  Giveway.govt.nz is actually a redirect.

Handled this by:

[Test]
        public void RunSpider_GivenAStartingWebsiteAnd20Jumps_ReturnAListOfWebsitesVisitedWhichShouldBeUniqueAndHandleCasesWhereAWebsiteIsARedirectByRevertingBackToLastWebsiteAndGoingToNextLink()
        {
            Spider s = new Spider();
            //startingSite = "http://www.giveway.govt.nz";
            var listOfSitesVisited = s.RunSpider(startingSite, 20);
            CollectionAssert.Contains(listOfSitesVisited, startingSite);

            CollectionAssert.AllItemsAreUnique(listOfSitesVisited);
        }

just by reverting to previous site.

Going too deep

image

getting bogged down and in an infinite loop… try and kiss as I want to explore.

set a limit of 30 chars.

Running Out of Suitable Links

image

m.twitter problem

..took out mobile site handling.. just go for it anyway

404 Exception

try catch go to stuff.co.nz

Frames problem with mongolia site

ahh

davemateer.com problem – no links

no external links on front page.

Features

image

Websites visited in order – starting with holidayhouses.co.nz

image

bbc.co.uk start.  With a 404 in the middle, which lead back to stuff.co.nz

image

starting with cnn.com I get here after 80 hops Smile

image

cnn start.

Ideas:

display on a map where servers are?

display on map which countries visited (by domain name extension)

See WPF post for UI that I did..

| | # 
# Monday, 07 March 2011
[TestMethod]
public void UnitTestThings()
{
Thing thing = new Thing();
IEnumerable<Thing> listOfThings = thing.giveMeAllThings();
Assert.AreEqual(3, listOfThings.Count()); // linq count
Assert.IsTrue(listOfThings.Any(t => t.name == "phone" && t.age == 3));
Assert.IsTrue(listOfThings.Any(t => t.name == "waterbottle" && t.age == 2));
Assert.IsTrue(listOfThings.Any(t => t.name == "pinecone" && t.age == 17));
}

Collection assertions http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.collectionassert.aspx

this is different from Contains:

[TestMethod]
public void when_asked_for_all_of_the_movies_it_should_receive_a_set_containing_each_movie_in_the_library()
{
Movie first_movie = new Movie();
Movie second_movie = new Movie();
movie_collection.Add(first_movie);
movie_collection.Add(second_movie);

MovieLibrary movieLibrary = new MovieLibrary(movie_collection);

IEnumerable<Movie> all_movies = movieLibrary.all_movies();

Assert.IsTrue(all_movies.Contains(first_movie));
Assert.IsTrue(all_movies.Contains(second_movie));
}

| | # 
[TestMethod]
public void when_sorting_movies_it_should_be_able_to_sort_all_movies_by_title_descending()
{
populateTestMovies(movie_collection);
MovieLibrary movieLibrary = new MovieLibrary(movie_collection);
IEnumerable<Movie> results = movieLibrary.sort_all_movies_by_title_descending();

IEnumerable<string> expected = new[]
{
theres_something_about_mary.title,
the_ring.title,
shrek.title,
pirates_of_the_carribean.title,
indiana_jones_and_the_temple_of_doom.title,
cars.title,
a_bugs_life.title
};
Assert.IsTrue(results.Select(m => m.title).SequenceEqual(expected));
}

thanks to http://stackoverflow.com/questions/5218505/assert-that-a-collection-is-in-the-right-order

| | # 
# Tuesday, 12 October 2010
( BDD | Testing )

http://should.codeplex.com/

image

It is open source.  This is somewhat of a reminder to myself to go and check out the code too.

| | # 
# Wednesday, 21 October 2009
Customer tests confirm how the feature is supposed to work as experienced by the end user.

Because customers generally don't want to use nUnit.. we're going to use Fitnesse, which is more user friendly:

The goal is to have the customer write their own tests.

To setup Fitnesse.  Download the .jar and run it like this:

c:\apps\fitnesse -jar fitness.jar -p 8080

Then go and get slim for .net.  I installed it to c:\apps\slim



Here is how it works for the Divider example:

!define TEST_SYSTEM {slim}
!define COMMAND_PATTERN {%m -r fitSharp.Slim.Service.Runner,c:\apps\slim\fitsharp.dll %p}
!define TEST_RUNNER {c:\apps\slim\Runner.exe}
 
!path C:\code\slimexample\slimexample\bin\Debug\slimexample.dll

|import      |
|slim_example|

!|Divider|
|Numerator|Denominator|Quotient?|
|10       |2          |5      |
|12.6     |3          |4.2      |
|22       |7          |~=3.14   |
|9        |3          |<5       |
|11       |2          |4<_<6    |
|100      |4          |33       |

Here is the C# code, I compiled to slimexample.dll
using System;

namespace slim_example
{
    public class Divider
    {
        public double  Numerator { get; set; }
        public double Denominator { get; set; }
        double _quotient;

        public void Execute()
        {
            _quotient = Numerator / Denominator;
        }

        public double Quotient()
        {
            return _quotient;
        }
    }
}
Run the tests.. and we'll see that when 2 numbers are sent... then then ? in the Fitnesse means that something should be checked.


Then we wire up to our code and test CatalogAdapter:

From this test we can see that I've a bug.. something wrong with the Duration.



Debugging

Have been working through why we are not getting the duration through.  The unit tests on the DAL are passing fine.. in CatalogFixture, have added in 2 tracks of different lengths.  They get pushed into the db, then pulled out using Catalog.. looks ok.



The next step is to look at the Service layer and see if the Duration is being written.  Interestingly, the webservice is passing back all the correct tracks, but nothing to do with reviewers etc..




Very interesting bug.. if I didn't specify this in my RecordingAssembler.WriteTotalRunTime:

dto.totalRunTimeSpecified = true;

then nothing would be passing back.  Same for WriteAverageRating.


Came across another issue with the webservice, and couldn't get it to autogen.. easiest way was to create it again.  Using source control so it if all went to custard, could go back.

Been getting bogged down in detail, so have decided to push to the end of the project, and get a front end up and running asap.



| | # 
# Tuesday, 20 October 2009
Task:  create a WebService that returns a recording and all its entities when specifying the id of the recording. Return type of XML (ie not a .net dataset).

The DAL.Catalog returns a RecordingDataSet.Recording.

We need to write a DTO that takes an input and transforms(munges) it into something else

Using a Service Interface Pattern.

Build a stub, map it into a DTO, and verify each field.
First thing I did was to create a stub, to hide how the RecordingDataSet is retrieved..so am not relying on the DAL at this point.

Here is the test the Stub code:
[TestFixture]
  public class CatalogServiceStubFixture
     {
         RecordingDataSet.Recording recording;
         RecordingDataSet.Recording actual;
         CatalogServiceStub service;
 
         [SetUp]
         public void SetUp()
         {
             // create a test recording in memory
             recording = CreateRecording();
             service = new CatalogServiceStub(recording);
             actual = service.FindByRecordingId(recording.Id);
         }
 
         private RecordingDataSet.Recording CreateRecording() 
         {
             RecordingDataSet dataSet = new RecordingDataSet();
             RecordingDataSet.Recording recording = dataSet.Recordings.NewRecording();
             recording.Id = 1;
            // more code needed here to fill in the rest of the recording.
             return recording;
         }
 
         [Test]
         public void CheckId()
         {
             Assert.AreEqual(recording.Id, actual.Id);
         }

Next we want a dto to transform our dataset, into something else:



To specify in platform independant, we use XML.  This is still quite bulky, perhaps JSON would be lighter now.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://nunit.org/webservices" elementFormDefault="qualified" targetNamespace="http://nunit.org/webservices" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Recording" type="tns:RecordingDto" />
  <xs:complexType name="RecordingDto">
    <xs:sequence>
      <xs:element minOccurs="1" maxOccurs="1" name="id" type="xs:long" />
      <xs:element minOccurs="1" maxOccurs="1" name="title" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="artistName" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="releaseDate" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="labelName" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="unbounded" name="tracks" type="tns:TrackDto" />
      <xs:element minOccurs="0" maxOccurs="unbounded" name="reviews" type="tns:ReviewDto" />
      <xs:element minOccurs="0" maxOccurs="1" name="totalRunTime" type="xs:int" />
      <xs:element minOccurs="0" maxOccurs="1" name="averageRating" type="xs:int" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="TrackDto">
    <xs:sequence>
      <xs:element minOccurs="1" maxOccurs="1" name="id" type="xs:long" />
      <xs:element minOccurs="1" maxOccurs="1" name="title" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="artistName" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="duration" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="genreName" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="ReviewDto">
    <xs:sequence>
      <xs:element minOccurs="1" maxOccurs="1" name="id" type="xs:long" />
      <xs:element minOccurs="1" maxOccurs="1" name="reviewerName" type="xs:string" />
      <xs:element minOccurs="1" maxOccurs="1" name="rating" type="xs:int" />
      <xs:element minOccurs="1" maxOccurs="1" name="reviewContent" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>


A benefit is that we can create C# from this:.. but really just getters and setters.  See below.. this led to an interesting bug.

http://www.codingday.com/xml-c-class-generator-for-c-using-xsd-for-deserialization/

xsd asdf.xml - create an xsd
xsd asdf.xsd /CLASSES  - creates a .cs

Then just creating an 'Assembler', which writes out the DTO...(from http://martinfowler.com/eaaCatalog/dataTransferObject.html)

The Assembler has business logic in it to flatten the structure (as in the similar example above)

The webservice is setup by adding it into IISAdmin.

As the default ASP.NET user does not have access to my local database
connection = new SqlConnection("Data Source=DAVEXPLAPTOP;Initial Catalog=catalog;Integrated Security=True");

The easiest way to get running on dev was to add the DAVEXPLAPTOP\ASPNET user into my SQL database.  This didn't show up under objects when adding, but I went ahead anyway and it worked.







So this is what we have so far:


| | # 
From the nbdn course, I had some interesting code I'd like to test.  Just the business logic really, no DAL nor UI.  As I wanted to make a full application, the first though was to make a DAL which talked to a text file, or just some dummy data.  How to do this well?  De-coupled.  Tried to use poor mans dependency injection, and got confused.  So I had a coffee, and bought this book.  Because it had an end to end example of a testable application.  It uses .net1.1 mostly (which is good as there are no language constructs to learn)



Some good introduction to refactorings, and unit testing.  The main meat of the book is an example called MediaLibrary (music recordings).  Chapter 5 started with testing the DAL.

Here are the first tests.  Am showing nunit test runner.. mostly I use testdriven.net, and have setup ctrl alt shft t to run tests.



for source control I'm using  git, and have coupled up with gvim as text editor.  And linked in beyond compare for a merge tool.

The book uses Strongly Typed Datasets, which were the 'star of the show' in the .net1.0 release.  By writing the sql, and having intellisense have autoproperties, this saves writing boiler plate code.

I learned about DataSets.. here is my edit (from wikipedia)..http://en.wikipedia.org/wiki/ADO.NET#DataSets

ADO.NET consists of two primary parts:

Data provider

These classes provide access to a data source, such as a Microsoft SQL Server.  Each data source has its own set of provider objects, but they each have a common set of utility classes:

  • Connection: Provides a connection used to communicate with the data source.
  • Command: Used to perform some action on the data source, such as reading, updating, or deleting relational data.
  • Parameter: Describes a single parameter to a command. A common example is a parameter to a stored procedure.
  • DataAdapter: A bridge used to transfer data between a Data source and a DataSet object (see below).
  • DataReader: Used to efficiently process a large list of results one record at a time. It allows records to be accessed in a read-only, forward-only mode, i.e., records have to be accessed in sequential order; they can neither be randomly accessed nor can a record which has been processed previously be accessed again.

DataSets

DataSet objects, a group of classes describing a simple in-memory relational database,

  • A DataSet object represents a schema (either an entire database or a subset of one). It can contain tables and relationships between those tables.
    • A DataTable object represents a single table in the database. It has a name, rows, and columns.
      • A DataView object overlays a DataTable and sorts the data (much like an SQL "order by" clause) and filters the records (much like an SQL "where" clause) if a filter is set. An in-memory index is used to facilitate these operations. All DataTables have a default filter, while any number of additional DataViews can be defined, reducing interaction with the underlying database and thus improving performance.
        • A DataColumn represents a column of the table, including its name and type.
        • A DataRow object represents a single row in the table; it allows reading and updating of values in that row, likewise retrieving any rows that are related to it through a primary-key foreign-key relationship.
VS has a nice editor:



Once I spiked up what datasets were (in simpleDatabaseTests.cs).. used this for reference: http://www.csharp-station.com/Tutorials/AdoDotNet/Lesson05.asp
 [Test]
        public void put_data_into_a_dataset()
        {
            DataSet dataset = new DataSet();
            SqlDataAdapter data_adapter = new SqlDataAdapter("select id, name from Artist", connection);
            SqlCommandBuilder sql_command_builder = new SqlCommandBuilder(data_adapter);
            data_adapter.Fill(dataset, "Artist");
            Assert.IsNotNull(dataset);

            DataTable data_table = dataset.Tables[0];
            for (int i = 0; i < data_table.Rows.Count; i++)
            {
                DataRow data_row = data_table.Rows[i];
                Console.Out.Write(data_row["id"] + " ");
                Console.Out.WriteLine(data_row["name"]);
            }
        }

The example I'm working through uses a Table Data Gateway Pattern for the DAL. 

http://martinfowler.com/eaaCatalog/tableDataGateway.html
"A Table Data Gateway holds all the SQL for accessing a single table or view: selects, inserts, updates, and deletes. Other code calls its methods for all interaction with the database."

Lets take an example of ArtistFixture.cs which tests ArtistGateway.cs

[TestFixture]
    public class ArtistFixture
    {
        static readonly string artistName = "Artist";
        SqlConnection connection;
        ArtistGateway gateway;
        RecordingDataSet recordingDataSet;
        long artistId;

        [SetUp]
        public void setup_and_open_connection_pass_to_gateway_setup_data_set()
        {
            connection = new SqlConnection(ConfigurationSettings.AppSettings.Get("Catalog.Connection"));
            connection.Open();

            recordingDataSet = new RecordingDataSet();
            gateway = new ArtistGateway(connection);
            // insert a new artist getting its ID from the database
            artistId = gateway.Insert(recordingDataSet, artistName);
        }

        [Test]
        public void RetrieveArtistFromDatabase()
        {
            // create new RDS, use same gateway.
            RecordingDataSet loadedFromDB = new RecordingDataSet();
            RecordingDataSet.Artist loadedArtist = gateway.FindById(artistId, loadedFromDB);

            Assert.AreEqual(artistId, loadedArtist.Id);
            Assert.AreEqual(artistName, loadedArtist.Name);
        }
Here is part of ArtistGateway.cs.. gateway is newed up with a connection passed in.. then only in Insert is it passed in a strongly typed RecordingDataSet, which has all the SQL in it which has been auto-genned.
public class ArtistGateway
{
SqlDataAdapter adapter;
SqlConnection connection;
SqlCommand command;
SqlCommandBuilder builder;

public ArtistGateway(SqlConnection connection)
{
this.connection = connection;

command = new SqlCommand("select id, name from artist where id = @id",connection);
command.Parameters.Add("@id", SqlDbType.BigInt);

adapter = new SqlDataAdapter(command);
builder = new SqlCommandBuilder(adapter);
}


public RecordingDataSet.Artist FindById(long artistId, RecordingDataSet recordingDataSet)
{
command.Parameters["@id"].Value = artistId;
adapter.Fill(recordingDataSet, recordingDataSet.Artists.TableName);
DataRow[] rows = recordingDataSet.Artists.Select(String.Format("id={0}", artistId));

if (rows.Length < 1) return null;
return (RecordingDataSet.Artist) rows[0];
}
Class Diagram done in VS2008 with the Class Designer Powertoy http://www.codeplex.com/modeling
To get the blue lines go to Class Diagram, Filter Lines, Show All Associations.

Sequence diagram done in Visio: (hmm - this is a bit complex... I prefer the class diagram above with notes)


Genre was next, then the rest.  Then relationships between objects eg ReviewReviewer.. just testing the test data we create comes back:  Have abstracted out some of the setup code into ConnectionFixture.
 [TestFixture]
 public class ReviewReviewerFixture : ConnectionFixture
 {
     [Test]
     public void ReviewerId()
     {
         RecordingDataSet recordingDataSet = new RecordingDataSet();

         ReviewGateway reviewGateway = new ReviewGateway(Connection);
         long reviewId = reviewGateway.Insert(recordingDataSet, 1, "Review Content");

         ReviewerGateway reviewerGateway = new ReviewerGateway(Connection);
         long reviewerId = reviewerGateway.Insert(recordingDataSet, "Reviewer Name");

         RecordingDataSet.Review review = reviewGateway.FindById(reviewId, recordingDataSet);

         review.ReviewerId = reviewerId;
         reviewGateway.Update(recordingDataSet);

         Assert.AreEqual(reviewerId, review.Reviewer.Id);

         reviewGateway.Delete(recordingDataSet, reviewId);
         reviewerGateway.Delete(recordingDataSet, reviewerId);
     }
 }
The hardest test is RecordingGateway, as it depends on all the other 'tables'.  To simplify, we abstract out the insert/del of test data to RecordingBuilder.cs.

The last class is Catalog, which is the only class that will be called from the Service layer.  In Catalog we have FindByRecordingId, which returns a RecordingDataSet.Recording (so all the data associated with this recordingId).




In summary we have done this of the overall structure of the APP:

| | # 
# Friday, 17 July 2009
A simple MVC (Model View Controller) implementation..code by Jimmy Chandra
http://stackoverflow.com/questions/1107720/mvc-c-simplest-possible-implementation

Why use MVC?  Similar reasons to MVP - testability and seperation of concerns

Simple.MVC.zip (167.25 KB)



1. Program.cs runs, running Main.. as is standard in a Win Forms app.  Difference from MVP is that main here is instantiating and running a controller class first of all, instead of newing up the view, which calls the controller/presenter.
static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            
            TopEmployeeController controller = new TopEmployeeController();
            Application.Run(controller.View as Form);
        }
2. Controller is instantiated and constructor called:
        private ITopEmployeeView _view;
        private Employee _employee;

        private bool _monitoring;

        public TopEmployeeController()
        {
            _employee = new Employee();
            _view = new TopEmployeeForm(this, _employee);
        }
3. Controller news up an employee (dumb).. then a view of type TopEmployeeForm, passing its self (the controller) and employee to it.

4. View (the observer) subscribes to the model (the subject) so that when model.OnPropertyChange is called, view.UpdateView is called.  This is the observer pattern, made easier in C# using events.. which is using a delegate.
// view
public TopEmployeeForm(ITopEmployeeController controller, Employee model)
        {
            _controller = controller;
            _model = model;

            //Let the model know that this view is interested if the model change
            _model.OnPropertyChange += new Action(UpdateView);

            InitializeComponent();
}
6. When Button is pressed on the view.  The controller.GetTopEmplyee method is called.  This calls model.MontorChanges:
// controller
public void GetTopEmployee()
{
if (!_monitoring)
{
_monitoring = true;
_employee.MonitorChanges();
}
}



7. MonitorChanges does some waiting code, then every second calls FirePropertyChange(). Which calls model.OnPropertyChange.
Which is really a delegate to View.UpdateView
// model
public event Action OnPropertyChange;

private void FirePropertyChange()
        {
            var propChange = OnPropertyChange;
            if (propChange != null)
            {
                OnPropertyChange();
            }
        }

8. The view method which runs every second and displays the name on the form.
// view
        public void UpdateView()
        {
            // to do with threading
            if (this.InvokeRequired)
            {
                this.Invoke(new Action(UpdateView));
            }
            else
            {
                TopEmployeeName = _model.FullName;
            }
        }




| | # 
# Thursday, 16 July 2009
( MVP | Patterns | Testing )
Easy to understand, working example WinForms app using MVP (Model View Presenter) pattern.

Why use this pattern?  Testability and maintainability (ie loosely coupled so less likely to break!)

Download noddy.zip (106.62 KB) (VS2008)

Notes:
View is dumb and does render logic
View never talks to the model (or any business entity eg customer) directly
Presenter retries data and manipulates
Model is the data and business rules

usually MVP sits on top of an ORM or Database Abstraction / Business Logic layer:


How it works:

1) Application comes to Program.cs which calls Form1.cs.. no MVP code here.
static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// Standard code.. nothing MVP
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }

2) Form1 (which is the view in MVP) which implements IView interface (**why**).. instantiates a new MainPresenter calling it presenter.
public partial class Form1 : Form, IView
    {
        private MainPresenter presenter;

        public Form1()
        {
            InitializeComponent();
             presenter = new MainPresenter(this);
        }

        private void btnPostCustomer_Click(object sender, EventArgs e)
        {
            presenter.PostCustomer();
        }

        // IView members
        public string FirstName
        {
            get { return txtFirstName.Text; }
            set { txtFirstName.Text = value; }
        }

        public string LastName
        {
            get { return txtLastName.Text; }
            set { txtLastName.Text = value; }
        }

        public void DisplayResult(string result)
        {
            MessageBox.Show(result);
        }
    }
3) MainPresenter constructor runs, and is passed in the view object of type IView. (**could we have just passed it in as a Form object?**)

public class MainPresenter
    {
        private IView view;
        private MainModel model = new MainModel();

        public MainPresenter(IView view)
        {
            this.view = view;    
        }

        public void PostCustomer()
        {
            Debug.Assert(view != null);

            try
            {
                Customer customer = new Customer(view.FirstName, view.LastName);
                model.PostCustomer(customer);
                view.DisplayResult(customer.ToString() + " posted");
            }
            catch(Exception ex)
            {
                view.DisplayResult(ex.Message);
            }
        }
    }
4) App is run, names are filled in, and Post button is submitted.



5) View code (form1.cs) runs presenter.PostCustomer
private void btnPostCustomer_Click(object sender, EventArgs e)
        {
            presenter.PostCustomer();
        }
6) presenter creates a new customer (which could be seen as being outside of MVP, and more in an entity later, as the model layer is usually light, and only relating the modelviews) and passes in the names.  Nothing special on the constructor of customer. 

public void PostCustomer()
        {
            Debug.Assert(view != null);

            try
            {
                Customer customer = new Customer(view.FirstName, view.LastName);
                model.PostCustomer(customer);
                view.DisplayResult(customer.FullName() + " posted");
            }
            catch(Exception ex)
            {
                view.DisplayResult(ex.Message);
            }
        }
7) Presenter calls the main model to PostCustomer which writes out to disk the Customer which has just been entered.
public void PostCustomer(Customer customer)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Customer));

            if (File.Exists("Customer.xml"))
            {
                using (FileStream stream = new FileStream("Customer.xml", FileMode.Append, FileAccess.Write))
                {
                    serializer.Serialize(stream, customer);
                }
            }
            else
            {
                using (FileStream stream = new FileStream("Customer.xml", FileMode.Create, FileAccess.Write))
                {
                    serializer.Serialize(stream, customer);
                }
            }
        }



8) Presenter calls the view to DisplayResult, passing in the customer data as a string.
public void DisplayResult(string result)
        {
            MessageBox.Show(result);
        }


Included in the download are simple tests on the ViewModel and Domain model.  Also Form2 which has a Form2Presenter.  Uses same ViewModel (hmm probably should have a seperate one), and the same domain model (customer.cs).  Next step for me is to get tests working on the Presenters.




| | # 
# Friday, 16 January 2009
( Testing )



Girl and man looking towards Mt Cook, NZ.

To get nUnit working on VS 2008 Express, firstly download and install nUnit.

Make a new console application.  Add the 3 nunit references you see below



Open up the .csproj file.. in my case in: C:\code\stuff\HowNUnitInspiredConsole\HowNUnitInspiredConsole\HowNUnitInspiredConsole.csproj

Where you see... add in the two Startxxx lines.

 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

    <StartAction>Program</StartAction>
    <StartProgram>C:\Program Files\NUnit 2.4.8\bin\nunit.exe</StartProgram>
   
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>

Here is my first unit test for a web app:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Web.Script.Serialization;
using System.Web.Util;
using System.Net;
using System.Web;
using System.IO;
using System.Diagnostics;

namespace HowNUnitInspiredConsole
{
    class NUnitConsoleRunner
    {
        [STAThread]
        static void Main(string[] args)
        {
            NUnit.ConsoleRunner.Runner.Main(args);
        }
    }

    [TestFixture]
    public class FormTest
    {
        // 1 is dev / local
        // 2 is test
        int debugbit = 2;

        string targetUri = "";
        string targetUriNoHTTP = "";

        [SetUp]
        public void Init()
        {
            if (debugbit == 1)
            {
                targetUri = "http://192.168.139.128/drink/";
                targetUriNoHTTP = "192.168.139.128/drink/";
            }

            if (debugbit == 2)
            {
                targetUri = "http://www.davemateer.com/drink/";
                targetUriNoHTTP = "www.davemateer.com/drink/";
            }
        }

        [Test]
        public void helloWorld()
        {
            WebClient client = new WebClient();
            StreamReader reader = new StreamReader(client.OpenRead(targetUri + "test/helloWorld.php"));
            string responseFromServer = reader.ReadToEnd();

            Assert.AreEqual("Hello World", responseFromServer);
        }
    }
}



When the nUnit gui popped up, make sure it gets the correct .exe file.  I had to do a project add assembly, while the application was running.

Run your application which should pop up with something like this:


| | #