Roel van Lisdonk on May 11th, 2012

I wanted to run regular MSTest unit test written in C# to assert JavaScript code. There are several ways to accomplish this, but in this case I use JSTest.

Just add JSTest to your C# testproject with NuGet, now you can write tests like:

 

Test class

using JSTest;
using JSTest.ScriptLibraries;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestProject1.Helpers;

namespace TestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var script = new TestScript();

            // Arrange: Append the JavaScript code to test.
            string scriptContents = (new AssemblyHelper().GetContentsEmbededResourceFile("TestProject1.MvcApplication1.Scripts.Person.js"));
            script.AppendBlock(scriptContents);

            // Arrange: Append the JSTest asser library, so we can assert the test code.
            script.AppendBlock(new JsAssertLibrary());

            // Append "Act" JavaScript code.
            script.AppendBlock("var person1 = new Person('John Do', 32, 'Software Engineer');");

            // Assert.
            script.RunTest(@"assert.equal('Jonh Do test', person1.sayName());");
        }
    }
}

Running this test within Microsoft Visual Studio 2010 will result in a passed test.

 

JavaScript code to test

function Person(name, age, job)
{
    var privateField1 = 'test';
    this.name = name;
    this.age = age;
    this.job = job;
    this.privilegedMethod = function () { return privateField1; }
};

Person.prototype.sayName = function ()
{
    return this.name + ' ' + this.privilegedMethod();
};

Notes

- The privilegedMethod is used to access private fields on the Person class.

- You can set breakpoints in the JavaScript code!!

C# helper class

using System;
using System.IO;
using System.Reflection;

namespace TestProject1.Helpers
{
    public class AssemblyHelper
    {
        /// <summary>
        /// Read the contents of an embededresourcefile with the given name.
        /// </summary>
        /// <param name="resourceName">Name of the resource.</param>
        /// <returns></returns>
        public string GetContentsEmbededResourceFile(string resourceName)
        {
            if (string.IsNullOrWhiteSpace(resourceName)) { throw new ArgumentNullException("resourceName"); }

            string contents = string.Empty;
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            using (var reader = new StreamReader(stream))
            {
                contents = reader.ReadToEnd();
            }
            return contents;
        }
    }
}

Microsoft Visual Studio 2010

image

Tags: , ,

By using a log4net ConsoleAppender, you can write all log messages in your application to the console. These message will show up in the Microsoft Visual Studio output window. I needed a way to redirect the messages written to the console, so I could verify if the correct messages were send to the console. For this task I redirected the standard console output in my unit test:

 

Code

using System;
using System.IO;
using System.Text;
using log4net;
using log4net.Config;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            // Save original console output writer.
            TextWriter originalConsole = Console.Out;

            // Configure log4net based on the App.config
            XmlConfigurator.Configure();

            var builder = new StringBuilder();
            using (var writer = new StringWriter(builder))
            {
                // Redirect all Console messages to the StringWriter.
                Console.SetOut(writer);

                // Log a debug message.
                ILog logger = LogManager.GetLogger("Unittest logger");
                logger.Debug("This is a debug message");
            }

            // Get all messages written to the console.
            string consoleOutput = string.Empty;
            using (var reader = new StringReader(builder.ToString()))
            {
                consoleOutput = reader.ReadToEnd();
            }

            // Assert.
            string expected = "This is a debug message" + Environment.NewLine;
            Assert.AreEqual(expected, consoleOutput);

            // Redirect back to original console output.
            Console.SetOut(originalConsole);
        }
    }
}

App.config

 

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
  </configSections>
  <log4net>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%message%newline" />
      </layout>
    </appender>
    <root>
      <level value="DEBUG"/>
      <appender-ref ref="ConsoleAppender" />
    </root>
  </log4net>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

Microsoft Visual Studio

 

image

 

I used NuGet to add a reference to the latest log4net build.

Tags: , ,

Roel van Lisdonk on May 5th, 2012

Well there are several ways to accomplish this. In this post I used a freeware tool from Microsoft.

This tool will let you create a bootable DVD or USB stick based on a ISO image.

Just download and install the tool from:  http://images2.store.microsoft.com/prod/clustera/framework/w7udt/1.0/en-us/Windows7-USB-DVD-tool.exe

After installing start the program from the start menu:

image

 

Then follow the steps in the wizard:

image

 

image

 

image

 

image

 

image

 

Don’t forget to set the USB stick as first boot device in the BIOS.

Tags:

Roel van Lisdonk on April 13th, 2012

Found a good articale on JSON Serialization and Deserialization:

http://www.codeproject.com/Articles/272335/JSON-Serialization-and-Deserialization-in-ASP-NET

I used this article to create a class that serializes DataSet, DataTable and DataRow objects:

I just changed one aspect of the codeproject article, I used the regular expression "\\/Date\((-?\d+)\)\\/" instead of  “\\/Date\((\d+)\+\d+\)\\/”. The regular expression “\\/Date\((-?\d+)\)\\/” takes into account dates before epoch (1979-1-1).

/// <summary>
/// Contains JSON helper functions.
/// </summary>
public class JsonHelper
{
    /// <summary>
    /// Date time format, used to convert a datetime to a string.
    /// By default "yyyy-MM-dd HH:mm:ss".
    /// </summary>
    public string DateTimeFormat { get; set; }

    /// <summary>
    /// Initializes properties.
    /// </summary>
    public JsonHelper()
    {
        DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
    }

    /// <summary>
    /// Converts a DataSet to a JSON string.
    /// </summary>
    /// <param name="dataSet">The dataset to convert.</param>
    /// <exception cref="System.ArgumentException">Thrown when parameter [dataSet] is null.</exception>
    /// <returns>
    /// - JSON in the format [[[Table0Row0Column0,Table0Row0Column1],[Table0Row1Column0,Table0Row1Column1]],[[Table1Row0Column0,Table1Row0Column1],[Table1Row1Column0,Table1Row1Column1]]]
    /// - Empty string, when dataset contains no tables.
    /// - Empty string, when all tables contain no rows.
    /// </returns>
    public string ToJson(DataSet dataSet)
    {
        if (dataSet == null) { throw new ArgumentNullException("dataSet"); }

        StringBuilder result = new StringBuilder(string.Empty);
        if (dataSet.Tables.Count > 0)
        {
            result.Append("[");
            foreach (DataTable table in dataSet.Tables)
            {
                result.Append(ToJson(table));
            }
            result.Append("]");
        }
        return result.ToString();
    }

    /// <summary>
    /// Converts a DataTable to a JSON string.
    /// </summary>
    /// <param name="table">The dataset to convert.</param>
    /// <exception cref="System.ArgumentException">Thrown when parameter [table] is null.</exception>
    /// <returns>
    /// - JSON in the format JSON in the format [[Row0Column0,Row0Column1],[Row1Column0,Row1Column1]]
    /// - Empty string, when datatable contains no rows.
    /// </returns>
    public string ToJson(DataTable table)
    {
        if (table == null) { throw new ArgumentNullException("table"); }

        StringBuilder result = new StringBuilder(string.Empty);
        if (table.Rows.Count > 0)
        {
            result.Append("[");
            foreach (DataRow row in table.Rows)
            {
                result.Append(ToJson(row));
            }
            result.Append("]");
        }
        return result.ToString();
    }

    /// <summary>
    /// Converts a DataRow to a JSON string.
    /// </summary>
    /// <param name="row">The data row to convert.</param>
    /// <exception cref="System.ArgumentException">Thrown when parameter [row] is null.</exception>
    /// <exception cref="System.ArgumentException">Thrown when property [DateTimeFormat] is null, empty or contains only whitespaces.</exception>
    /// <returns>
    /// - JSON in the format [Row0Column0,Row0Column1].
    /// - Empty string, when datarow contains no columns.
    /// </returns>
    public string ToJson(DataRow row)
    {
        if (row == null) { throw new ArgumentNullException("row"); }
        if (string.IsNullOrWhiteSpace(DateTimeFormat)) { throw new ArgumentNullException("DateTimeFormat"); }

        StringBuilder result = new StringBuilder(string.Empty);
        if (row.ItemArray.Count() > 0)
        {
            var serializer = new JavaScriptSerializer();
            string json = serializer.Serialize(row.ItemArray);

            // Replace Date(...) by a string in the format found in the property [DateTimeFormat].
            var matchEvaluator = new MatchEvaluator(ConvertJsonDateToDateString);
            var regex = new Regex(@"\\/Date\((-?\d+)\)\\/");
            json = regex.Replace(json, matchEvaluator);

            result.Append(json);
        }
        return result.ToString();
    }

    /// <summary>
    /// Converts a JSON string to a object array.
    /// </summary>
    /// <param name="input">The input.</param>
    /// <exception cref="System.ArgumentException">Thrown when input is null.</exception>
    /// <returns></returns>
    public object[] FromJson(string input)
    {
        if (input == null) { throw new ArgumentNullException("input"); }
        var serializer = new JavaScriptSerializer();
        object[] result = serializer.Deserialize(input, typeof(object[])) as object[];

        return result;
    }

    /// <summary>
    /// Replace JSON dates, like "\/Date(1330740183000)\/" to a string in the format found in the property [DateTimeFormat].
    /// </summary>
    /// <param name="match">When null, throws exception</param>
    /// <exception cref="ArgumentNullException">Throws ArgumentNullException, when property [DateTimeFormat] is null, empty or contains only white spaces.</exception>
    /// <returns>A string in the format found in the property [DateTimeFormat]</returns>
    public string ConvertJsonDateToDateString(Match match)
    {
        if (match == null) { throw new ArgumentNullException("match"); }
        if (string.IsNullOrWhiteSpace(DateTimeFormat)) { throw new ArgumentNullException("DateTimeFormat"); }

        string result = string.Empty;
        DateTime dt = new DateTime(1970, 1, 1); // Epoch date, used by the JavaScriptSerializer to represent starting point of datetime in JSON.
        dt = dt.AddMilliseconds(long.Parse(match.Groups[1].Value));
        dt = dt.ToLocalTime();
        result = dt.ToString(DateTimeFormat);
        return result;
    }
}

 

Just an other tip on JSON, if you want to de-serialize a JSON string containing a object with field names, you can use:

string json = @"{Id:1,Barcode:""WWW12312345678""}";
var serializer = new JavaScriptSerializer();
var dict = serializer.Deserialize<Dictionary<string, string>>(json);
Console.WriteLine(dict["Barcode"]);

Tags:

Roel van Lisdonk on April 13th, 2012

If you want to get table schema / structure information with T-SQL, you have several options, the most commonly used by me are:

Using the system stored procedure sp_help

exec sp_help ‘<your table name>’

 

Using a custom query

-- Dump table schema / structure info
declare @tabelObjectId int
set @tabelObjectId = Object_ID(N'<Your table name>')

select
  'Column_name' = ac.name,
  'Type'        = type_name(ac.user_type_id),
  'Length'            = convert(int, ac.max_length),
  'Nullable'        = case when ac.is_nullable = 0 then 'No' else 'Yes' end,
  'Identity'    = case when ac.is_identity = 0 then 'No' else 'Yes' end,
  'PK'          = case when exists(
                    select 1 from sys.index_columns ic
                    inner join sys.columns c  on  ic.object_id = c.object_id
                                              and c.column_id = ic.column_id
                    where ic.object_id = @tabelObjectId and c.name = ac.name
                  ) then 'Yes' else 'No' end,
  'FK'          = case when exists(
                    select 1 from sys.foreign_key_columns fc
                    inner join sys.columns c  on  c.object_id = parent_object_id
                                              and c.column_id = fc.parent_column_id
                    where fc.parent_object_id = @tabelObjectId and c.name = ac.name
                  ) then 'Yes' else 'No' end
from sys.all_columns ac where ac.object_id = @tabelObjectId

Result for a table

 

Column_name Type Length Nullable Identity PK FK
Id int 4 No Yes Yes No
AddressId int 4 No No No Yes
Weekday smallint 2 No No No No
From time 5 Yes No No No
To time 5 Yes No No No
ResetDate date 3 Yes No No No

Tags:

If you want to start a batch file from an other batch file you can use the start command.

If you want to hide the command line window, that will be displayed by default, use the /B parameter.

 

Example

Test1.bat contents:

start /B /wait Test2.bat

exit

 

When you execute Test1.bat on the command line, the Test2.bat will be executed without showing a window.

(The Test1.bat will wait until Test2.bat is finished, because the /wait argument is supplied.)

Tags:

Roel van Lisdonk on April 2nd, 2012

Found the solution at:

http://stackoverflow.com/questions/653714/how-to-select-into-temp-table-from-stored-procedure

The following T-SQL code executes the system stored procedure sp_helpfile and stores the result in the table variable @temp.

 

declare @temp table
(
    name varchar(255),
    field varchar(255),
    filename varchar(255),
    filegroup varchar(255),
    size varchar(255),
    maxsize varchar(255),
    growth varchar(255),
    usage varchar(255)
);
INSERT @temp  Exec sp_helpfile;
select * from @temp;

Tags:

If you want to validate an XML file against a XSD file and use the XSD schemas referenced by the XSD file in the validation, you can use the following C# code:

 

using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Research.Rli
{
/// <summary>
/// Tests in this class are only used to analyse and research code.
/// It is not intended to contain real integration tests.
/// </summary>
[TestClass]
public class RliResearchTester
{
public RliResearchTester()
{
}

/// <summary>
/// Test xml validation against a XSD file.
/// </summary>
[TestMethod]
public void ValidateXmlTest()
{
    ValidateXml(@"C:\Data\Input.xml", @"C:\Data\ValidationSchema.xsd");
}
/// <summary>
/// Validates a xml file (found at the given xmlFilePath) to a XSD file (found at the given xsdFilePath).
/// </summary>
/// <param name="xmlFilePath">The xml file to validate.</param>
/// <param name="xsdFilePath">The xsd file used in the validation.</param>
/// <exception cref="ArgumentNullException">
/// Throws an ArgumentNullException, when xmlFilePath is null, empty or contains only white spaces.
/// </exception>
/// <exception cref="ArgumentNullException">
/// Throws an ArgumentNullException, when xsdFilePath is null, empty or contains only white spaces.
/// </exception>
/// <exception cref="ArgumentException">Throws an ArgumentException, when xmlFilePath does not exist.</exception>
/// <exception cref="ArgumentException">Throws an ArgumentException, when xsdFilePath does not exist.</exception>
/// <remarks>
/// - Includes schemas referenced by the given xsd file (recursively).
/// </remarks>
public void ValidateXml(string xmlFilePath, string xsdFilePath)
{
    if (string.IsNullOrWhiteSpace(xmlFilePath)) { throw new ArgumentNullException("xmlFilePath"); }
    if (string.IsNullOrWhiteSpace(xsdFilePath)) { throw new ArgumentNullException("xsdFilePath"); }
    if (!File.Exists(xmlFilePath))
    {
        throw new ArgumentException(string.Format("File [{0}] not found.", xmlFilePath));
    }
    if (!File.Exists(xsdFilePath))
    {
        throw new ArgumentException(string.Format("File [{0}] not found.", xsdFilePath));
    }

    var schemas = new XmlSchemaSet();

    // Use the target namespace specified in the XSD file.
    schemas.Add(null, xsdFilePath);

    var readerSettings = new XmlReaderSettings();

    // Validate the xml file to an XSD file not an DTD or XDR.
    readerSettings.ValidationType = ValidationType.Schema;

    // Include schemas referenced by the given xsd file (recursively) in the validation proces.
    readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;

    // Warnings will fire the ValidationEventHandler function.
    readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
    readerSettings.Schemas.Add(schemas);

    // The function [ValidationEventHandler] will be used to handle all validation errors / warnings.
    readerSettings.ValidationEventHandler += ValidationEventHandler;

    using (var xmlReader = XmlReader.Create(xmlFilePath, readerSettings))
    {
        while (xmlReader.Read()) { }    // Validate XML file.
    }
}
/// <summary>
/// This event will fire on every XML validation error / warning.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void ValidationEventHandler(object sender, ValidationEventArgs args)
{
    Console.WriteLine(string.Format("",
        new object[] { args.Exception, args.Exception.LineNumber, args.Exception.LinePosition }));
}
}
}

Tags:

When trouble shouting error’s thrown by T-SQL, naming your primary and foreign keys, proves to be handy.

When creating tables with T-SQL, I use the naming format PK_MyTable_MyColumn1_MyColumn2 and FK_MyTable_MyColumn3_MyColumn4:

if object_id('[dbo].[Address]') is null
begin
    create table [dbo].[Address]
    (
        Id        int not null  identity(1,1) constraint PK_Address_Id primary key
    )
end

if object_id('[dbo].[Person]') is null
begin
  create table [dbo].[Person]
  (
      Id           int not null  identity(1,1) constraint PK_Person_Id primary key,
      AddressId    int not null constraint FK_Person_AddressId foreign key references [dbo].[Address](Id)
  )
end

Tags: ,

For testing purposes it is possible to copy a new localdb database to your output directory and start using this localdb database. For more information see my previous post: http://www.roelvanlisdonk.nl/?p=2607

It is possible that a previous tests run did not cleanup it’s copy of the localdb database. Now if you want to attach an new localdb database with the same name, the attach will fail. There are several options you have to fix this error. I decided to just "re-use" the existing database, just changing the *.mdf and *.ldf file location.

 

The following code can help you do just that:

using System.Data.SqlClient;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace Test.Research
{
    /// <summary>
    /// Tests in this class are only used to analyse and research code.
    /// It is not intended to contain real integration tests.
    /// </summary>
    [TestClass]
    public class RliResearchTester
    {

        /// <summary>
        /// Changes the *.mdf and *.ldf file location for the given LocalDb database, when it exists.
        /// </summary>
        /// <remarks>
        /// It expects the mdf file name to be in the format [DatabaseName.mdf].
        /// It expects the ldf file name to be in the format [Databasename_log.ldf].
        /// It expects the old mdf file name to be equal to the new mdf file name (only the file location will change).
        /// It expects the old ldf file name to be equal to the new ldf file name (only the file location will change).
        /// </remarks>
        [TestMethod]
        public void ChangeMdfAndLdfLocationForExistingLocalDb()
        {
            string dataSource = @"(localdb)\V11.0";
            string databaseName = "MyReserachDb";
            string folderContainingNewDatabaseFiles = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string oldMdfFilePath = GetMdfFilePath(dataSource, databaseName);
            if (!string.IsNullOrWhiteSpace(oldMdfFilePath))
            {
                ModifyDatabaseFilePaths(dataSource, databaseName, folderContainingNewDatabaseFiles);
            }
        }
        /// <summary>
        /// Gets the full physical mdf filepath, based on the supplied data soruce (Servername\Instancename) and database name.
        /// </summary>
        /// <param name="dataSource">SQL Server instance in the format (ServerName\InstanceName)</param>
        /// <param name="databaseName">Name of the database.</param>
        /// <returns>
        /// Databse found:     Returns the full physical mdf filepath.
        /// Databse not found: Returns null.
        /// </returns>
        /// <remarks>
        /// Uses windows authentication to connect.
        /// This function does not take into account, the fact that a mdf file name can be different, then the [DatabaseName.mdf].
        /// </remarks>
        public string GetMdfFilePath(string dataSource, string databaseName)
        {
            string result = null;
            string mdfName = databaseName;
            using (var connection = new SqlConnection(string.Format(@"Data Source=""{0}"";Initial Catalog=master;Integrated Security=True;", dataSource)))
            using (var command = new SqlCommand("select top 1 physical_name from sys.master_files where name = @MdfName", connection))
            {
                command.Parameters.Add(new SqlParameter("MdfName", mdfName));

                connection.Open();
                result = command.ExecuteScalar() as string;
            }
            return result;
        }
        /// <summary>
        /// Modifies the *.mdf and *.ldf database file locations of an existing online database.
        /// </summary>
        /// <param name="dataSource">SQL Server instance in the format (ServerName\InstanceName)</param>
        /// <param name="databaseName">Name of the database.</param>
        /// <param name="folderContainingNewDatabaseFiles">The folder containing new database files.</param>
        /// <remarks>
        /// - Uses windows authentication to connect.
        /// - Drops all existing database connections, by using [with rollback immediate] on the [set offline].
        /// - Forces the database to use the new files, by using [set offline] and [set online].
        /// - It expects the mdf file name to be in the format [DatabaseName.mdf].
        /// - It expects the ldf file name to be in the format [Databasename_log.ldf].
        /// - It expects the old mdf file name to be equal to the new mdf file name (only the file location will change).
        /// - It expects the old ldf file name to be equal to the new ldf file name (only the file location will change).
        /// </remarks>
        public void ModifyDatabaseFilePaths(string dataSource, string databaseName, string folderContainingNewDatabaseFiles)
        {
            string mdfName = databaseName;
            string ldfName = string.Format("{0}_log", databaseName);
            string newMdfFileLocation = Path.Combine(folderContainingNewDatabaseFiles, string.Format("{0}.mdf", mdfName));
            string newLdfFileLocation = Path.Combine(folderContainingNewDatabaseFiles, string.Format("{0}.ldf", ldfName));
            string query = string.Format(@"
                alter database {0} modify file (name = {1}, filename = '{3}');
                alter database {0} modify file (name = {2}, filename = '{4}');
                alter database {0} set offline with rollback immediate;
                alter database {0} set online;",
                new string[] { databaseName, mdfName, ldfName, newMdfFileLocation, newLdfFileLocation });
            using (var connection = new SqlConnection(string.Format(@"Data Source=""{0}"";Initial Catalog=master;Integrated Security=True;", dataSource)))
            using (var command = new SqlCommand(query, connection))
            {
                connection.Open();
                command.ExecuteNonQuery();
            }
        }
    }
}

Tags: , ,