I am writing a small web shop application using ASP.NET MVC Framework. As a part of this project I also write unit test and integration tests. I am using LINQ To SQL Classes as a mapping solution in my project. I found one misleading behaviour of LINQ To SQL Classes when writing integration tests.
Look at these two test. They are same test but with little differences. Note: ProductRepository uses Linq To SQL DataContext internally. All changes are flushed to database after inserting, deleting and updating products. GetProduct() creates new product and initializes all properties. The test should fail with last Assert shown in both versions.
Passes
public void Should_insert_new_product()
{
var product = GetProduct();
var repository = new ProductRepository();
repository.Save(product);
Assert.AreNotEqual(0, product.Id);
var id = product.Id;
var savedProduct = (Product)repository.GetById(id);
//...
Assert.AreEqual(_listIcon, savedProduct.ListIcon);
//...
}
Fails
public void Should_insert_new_product()
{
var product = GetProduct();
var repository = new ProductRepository();
repository.Save(product);
Assert.AreNotEqual(0, product.Id);
var id = product.Id;
repository.Dispose();
repository = new ProductRepository();
var savedProduct = (Product)repository.GetById(id);
//...
Assert.AreEqual(_listIcon, savedProduct.ListIcon);
//...
}
Why should this test fail? The point simple – Product.ListIcon is currently just a property that is not bound to database. As it turns out then LINQ To SQL caches objects somewhere and is able to return object with the same ID from somewhere. When disposing repository then repository also disposes DataContext and everything gets cleaned up.
It turns out that for testing we should also dispose DataContext after each operation so we don’t face caching problems when we want to see if database operations work as expected.
One of ASP.NET MVC errors wasted one hour of my life today (of course, it is many times less than this season’s leader SharePoint) but this is still one hour. “The view 'Index' or its master could not be found. The following locations were searched” error raised suddenly and I was not able to get over it. Well, this time solution was simple.
The error was:
The view 'Index' or its master could not be found. The following locations were searched:
~/Views/Home/Index.aspx
~/Views/Home/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
It tells about page requested but doesn’t mention exactly what is missing. In my case it was one MvcUserControl that was missing. After inserting correct name for it the error disappeared and everything worked again.
Loggers are one of most popular examples about interfaces for sure. And there are a lot of implementations of loggers. Some implementations are simple and yet powerful, some implementations may be more complex. All we have to is to select implementation we need and integrate it to our application. It seems like good idea at first place but as soon as we need to switch from one implementation to another we discover nasty dependencies we have to change in all places where we are logging. Let’s see how to avoid these dependencies.
Analysis
To keep our application independent from logger implementation we need an interface that all our loggers will use. Our own loggers can implement our interface directly and for third-party loggers we can write wrapper classes that implement our interface. This way or other we logger interface.
Besides errors we may want to write also other messages to error log. We may need informative messages, warnings and debugging messages. To support different message types we need an enumerator.
Design
Our analysis gave us two objects: interface for loggers and enumerator for log message types. Our logger class looks like this.
In code we have ILogger interface and LogEntryType enumerator.
public interface ILogger
{
void Log(LogEntryType type, string message);
void Log(Exception exception);
}
public enum LogEntryType
{
Info,
Warning,
Error
}
LogEntryType is very simple here because it is for example purposes. In real life this enum may be way longer than here. Good example is TraceSeverity enum from my blog post SharePoint: Writing messages to ULS (Unified Logging System).
Implementing ILogger
Let’s create now two logger implementations. One of these classes is our own logger class and the other one will be wrapper for log4net logger.
DebugLogger
DebugLogger is simple logger that writes all log messages to console. It does nothing special – it writes all the output to debug window.
public class DebugLogger : ILogger
{
public void Log(LogEntryType entryType, string message)
{
var logMessage = GetLogMessage(entryType, message);
Debug.WriteLine(logMessage);
}
public void Log(Exception ex)
{
var logMessage = GetLogMessage(LogEntryType.Error, ex.ToString());
Debug.WriteLine(logMessage);
}
private string GetLogMessage(LogEntryType entryType, string message)
{
var messageBuilder = new StringBuilder();
messageBuilder.Append(DateTime.Now.ToLongTimeString());
messageBuilder.Append(" ");
messageBuilder.Append(Enum.GetName(typeof(LogEntryType), entryType));
messageBuilder.Append(" ");
messageBuilder.Append(message);
return messageBuilder.ToString();
}
}
Output of DebugLogger is shown on following screenshot.
Log4NetLogger
Log4NetLogger is simple wrapper class. It only writes messages to log4net log. All other details are handled by log4net.
public class Log4NetWrapper : ILogger
{
private static readonly ILog log = LogManager.GetLogger(typeof(Log4NetWrapper));
static Log4NetWrapper()
{
XmlConfigurator.Configure();
}
public void Log(LogEntryType entryType, string message)
{
lock (log)
{
switch (entryType)
{
case LogEntryType.Error:
log.Error(message);
break;
case LogEntryType.Warning:
log.Warn(message);
break;
default:
log.Info(message);
break;
}
}
}
public void Log(Exception ex)
{
lock (log)
{
log.Error(ex);
}
}
}
In the case of file output we get result like this.
Breaking dependencies
Now it’s time to add logging support to our application. All logging clients must see only ILogger interface and LogEntryType enum. Plus one thing more – they have to know how to get logger. We will use Unity as IoC container. We want to keep information about mappings in application configuration file. To make unity read this configuration we need some place where we can initialize Unity. Look at Main() method (and Unity configuration) from my blog entry Unity and singletons (it’s not long). Now, if we need some logging we can ask logger simply by writing the following code.
var logger = container.Resolve<ILogger>();
To keep Unity logic away from your classes you can use some wrapper class that uses Unity IoC container internally. Also it is possible to write generic wrapper for IoC containers so your code knows only one concrete IoC wrapper class. In this case IoC implementation details are hidden for your client code.
Last month I wrote about how to add SharePoint users for Live ID accounts on Community Kit for SharePoint. Another problem we found was that logout didn’t worked as expected. When logging out form Live ID we can see error on Live ID site stating that logout from our system failed.
I found a solution for this problem and it is very easy fix to apply.
- Open liveauth-handler.aspx.cs.
- Move to Render method.
- Paste the following code after first if.
else if (Request.QueryString["action"] == "clearcookie")
{
Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
var gif = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7";
Page.Response.Clear();
Page.Response.ClearHeaders();
Page.Response.ContentType = "image/gif";
Page.Response.BinaryWrite(Convert.FromBase64String(gif));
Page.Response.Flush();
Page.Response.End();
}
- Save handler file, compile the project and deploy compiled DLL to GAC.
I hope it helps somebody.
I’m sure you have seen classes that initialize a lot of objects in their constructor. These classes may be hard to test because of those object creations I mentioned. To get around this problem we use Extract and override factory method refactoring so we can extend these classes and override some factory methods.
Let’s see the following code.
public class XmlPaymentManager
{
private PaymentTranformer _transformer;
public XmlPaymentManager()
{
var readerPath = ConfigurationSettings.AppSettings["ReaderPath"];
var writerPath = ConfigurationSettings.AppSettings["WriterPath"];
var reader = new StreamReader(readerPath);
var writer = new StreamWriter(writerPath);
_transformer = new PaymentTranformer(reader, writer);
}
// ...
}
We can see here initialization of StreamReader and StreamWriter. We cannot use these classes for unit testing purposes because we don’t want to read and write files. If we do then we have integration tests as we may face file system issues. As a first step let’s move creation of PaymentTransformer class to separate method.
public class XmlPaymentManager
{
private PaymentTranformer _transformer;
public XmlPaymentManager()
{
_transformer = GetTransformer();
}
protected PaymentTranformer GetTransformer()
{
var readerPath = ConfigurationSettings.AppSettings["ReaderPath"];
var writerPath = ConfigurationSettings.AppSettings["WriterPath"];
var reader = new StreamReader(readerPath);
var writer = new StreamWriter(writerPath);
return new PaymentTranformer(reader, writer);
}
// ...
}
Now we can create extended class that overrider GetTransformer() method. For testing purposes we use FakePaymentTransformer.
public class TestXmlPaymentManager : XmlPaymentManager
{
protected PaymentTranformer GetTransformer()
{
return new FakeXmlPaymentTransformer();
}
}
Extract and override factory method helps you if you can extend the class you want to test. You may save a lot of time when you find larger hard to test classes and you can make them testable using this refactoring method.
On Friday Microsoft released March 2009 CTP of Windows Azure Tools for Microsoft Visual Studio. You have to remove previous releases of Azure tools before installing new one.
New stuff in CTP:
- Combined installer includes the Windows Azure SDK
- Addressed top customer bugs
- Native Debugging of Cloud Service Roles
- FastCGI Project Template
- Update Notifications of future releases
Windows Azure Tools for Microsoft Visual Studio includes:
- C# and VB Project Templates for building Cloud Services
- Tools to change the Service Role configuration
- Integrated local development via the Development Fabric and Development Storage services
- Debugging Cloud Service Roles running in the Development Fabric
- Building and packaging of Cloud Service Packages
- Browsing to the Azure Services Developer Portal
- SSL Certificate selection
So, I start installing now. :)
Sometimes you are not able to use Extract Interface refactoring because interface of parameter may be too large or there may be no interface at all. You just have class to pass in and you are not able to fake it or extend it to get class testable. In this case you have to use Adapt Parameter refactoring to create adapter that is seen through common some common class of interface.
Let’s look at the following code.
public class CustomerCardService
{
//...
public bool IsValidCustomer(MagneticStripeCardReader reader)
{
var customerCode = reader.ReadCustomerCode();
var expiryDate = reader.ReadExpiryDate();
var customer = _repository.GetByCode(customerCode);
if (customer == null)
return false;
return (expiryDate > DateTime.Now);
}
// ...
}
Here we can see a method that accepts class MagneticStripeCardReader. This class implements no interfaces and we are not able to extend it. We cannot fake it and we cannot go to interfaces. To get over this problem let’s create interface that we can use to make this method testable.
public interface ICustomerDataReader
{
string ReadCustomerCode();
DateTime ReadExpiryDate();
}
As a next thing we have to modify this method to make it accept our new interface. All we have to do is to change parameter signature.
public bool IsValidCustomer(ICustomerDataReader reader)
{
// ...
}
After that let’s write fake class we can use to test this method.
public class FakeCardReaderSource : ICustomerDataReader
{
private string _customerCode;
private DateTime _expiryDate;
public FakeCardReader(string customerCode, DateTime expiryDate)
{
_customerCode = customerCode;
_expiryDate = expiryDate;
}
public string ReadCustomerCode()
{
return _customerCode;
}
public DateTime ReadExpiryDate()
{
return _expiryDate;
}
}
Now we are almost finished. All we need to do is to write adapter class for MagneticStripeCardReader.
public class MagneticStripeSource : ICustomerDataReader
{
private MagneticStripeCardReader _reader;
public MagneticStripeSource(MagneticStripeCardReader reader)
{
_reader = reader;
}
public string ReadCustomerCode()
{
return _reader.ReadCustomerCode();
}
public DateTime ReadExpiryDate()
{
return _reader.ReadExpiryDate();
}
}
Besides testability we achieved one thing more – we can easily create adapters for other types of card reader classes (by example, we may have SmartCardReader in the future). In current case we were happy – we had made no modifications to original method body. But in real world we usually have to modify also method body.
After installing Windows Server 2008 to one of my development server I got the following error: Windows could not be activated. Exact details of error:
| Error: | Windows could not be activated. |
| Code: | 8007232B |
| Description: | DNS name does not exist. |
As it turns out this is problem with volume licensing. You have to activate your Windows by inserting key again (Control Panel –> System). After that activation succeeds. As you can read from Chapter Zero then this error occurs also with Windows Vista.
It is possible to solve name.dll issue in SharePoint without modifying JavaScript files.
- Open master page in SharePoint Designer.
- Find the following line:
<asp:ContentPlaceHolder id="PlaceHolderAdditionalPageHead" runat="server"/>
- Add the following script block after it:
<script>
function ProcessImn() {}
</script>
- Check the master page in and publish it.
This solution worked well for me using BlueBand.master. I think it is not an good idea to modify script files as SharePoint updates may also update the scripts.