Archive for the ‘C#’ Category
July 16th, 2009
A colleague of mine and I were discussing good verb replacements for “GetOrCreate” data retrieval patterns, where the “Create” part is responsible for the instantiation of a new instance of something. While the pattern I present here did not solve his particular problem, its at least worth sharing. Its obvious, and probably in use commonly already, but I figure its worth noting.
Essentially the goal is to get away from a “GetOrCreate” master method call and just have two methods: Get(args) and Get(args, Foo default) and then leave the responsibility of default generation up to Foo. This way there’s no unintended side effects and you don’t have to explicitly coalesce (though with this pattern you could as easily coalesce; strictly speaking it isn’t as “discoverable”)…
var foo = FooService.Get(23) ?? new Foo { ... };
So here’s the pattern defined in code:
public class Foo : ICloneable
{
private readonly static Foo __default =
new Foo { Bar = "...", Baz = Int32.MinValue };
public string Bar { get; set; }
public int Baz { get; set; }
public static Foo Default()
{
return (Foo)__default.Clone();
}
public object Clone()
{
return new Foo { Bar = this.Bar, Baz = this.Baz };
}
}
public class FooService
{
public Foo Get(int baz)
{
// retrieve from data store...
return null;
}
public Foo Get(int baz, Func<Foo> @default)
{
return Get(baz) ?? @default.Invoke();
}
}
You’ll see that any associated overhead with Clone only occurs if Get(baz) returns null. I have specifically stepped around the common naming standard of “GetDefault()” for the function – its named like a property - because its intended usage is as follows:
var foo = new FooService().Get(12, Foo.Default);
An ICloneable reference type is pretty much required, though any immutable reference type could get by without cloning because any changes gives you a new copy of that type.
April 29th, 2009
Just making sure this is correct. By this I mean PreCode (requires Windows Live Writer 2009) with SyntaxHighligher setup. PreCode is also a stand alone program.
public bool Validate(IValidationDictionary modelState, string prefix)
{
// xVal example code
var dataAnnotationErrors = from prop in TypeDescriptor.GetProperties(_entity).Cast<PropertyDescriptor>()
from attribute in prop.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(prop.GetValue(_entity))
select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), _entity);
var brokenRules = GetBrokenRules();
if (!String.IsNullOrEmpty(prefix))
prefix += ".";
if (dataAnnotationErrors.Any())
dataAnnotationErrors.ForEach(ei => modelState.AddError(prefix + ei.PropertyName, ei.ErrorMessage));
if (brokenRules.Any())
brokenRules.ForEach(rule => modelState.AddError(prefix + rule.Property, rule.Message));
return modelState.IsValid;
}
There is a “Fix Indentation” button in PreCode. I am in love.
Now just to setup the clipboard SWF thing.
I should seriously consider a theme with a wider content area.
March 30th, 2009
The Lightweight Test Automation Framework is a browser automation framework like WatiN and Selenium RC that allows you to create automated integration tests. Its a beta release from MS and can be found on Codeplex at:
http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=22739
I first read about it from Steve Sanderson's blog where he describes how to use it with an ASP.NET MVC project.
I am supporting a legacy web forms application and have a typical web application setup so I'll explain what I did to get up and running in a matter of minutes with LTAF.
- Download LTAF; you can use the "... for ASP.NET Samples" link and pull the *.dll out of the Bin folder and the contents of the Test folder, or you could build from source using the "... for ASP.NET Source"
- Create a new Class Library project in your solution and name it appropriately - Company.Tests.Integration.Site - for example
- For this project's properties alter the output assembly's name to begin with App_Code - this will slightly speed up web test load times and can help solve problems you may experience if your web application references tons of assemblies, i.e. App_CodeCompany.Tests.Integration.Site
- Add a reference to Microsoft.Web.Testing.Light.dll to your Company.Tests.Integration.Site project
- Copy and add to your web project (Company.Site) the Test folder; you can name the folder anything you want and you don't strictly have to add it to your project but I did for convenience for Start > Debug
- Remember to alter your build process to remove the Test folder for production releases
- Alter the web.config file for your Company.Site to allow anonymous access to the Test folder:
<location path="Test">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
- Again, remember to alter your build process to eliminate this node from your production web.config
- Add a reference to your integration test project, Company.Tests.Integration.Site, to your web application, Company.Site
- Let's test the login page (here I assume that "Login.aspx" is the name of your login page and its in the root of the application and you've used an <asp:Login /> control)...
- Create a new class LoginTests in your Company.Tests.Integration.Site project and eliminate the file's contents - it is very important that all tests exist outside of a namespace
- Paste this code into the body of your LoginTests.cs file:
using Microsoft.Web.Testing.Light;
[WebTestClass]
public class LoginTests
{
[WebTestMethod]
public void SignInAndSignOut()
{
var page = new HtmlPage("Login.aspx");
page.Elements.Find("UserName").SetText("someuser");
page.Elements.Find("Password").SetText("yourPW");
page.Elements.Find("LoginButton").Click(WaitFor.Postback);
// you'll need to determine what a good landmark for your
// application is upon successful login
var logoutLink = page.Elements.Find("LogoutHyperlink");
Assert.AreEqual("[Logout]", logoutLink.GetInnerText());
logoutLink.Click(WaitFor.Postback);
Assert.IsNotNull(page.Elements.Find("LoginButton"));
}
}
- Set your Company.Site as the startup project if its not already, and Tests/Default.aspx as the startup page; run from Visual Studio
Magic! This is my first use of a browser automation framework for integration testing and while it has its pitfalls, it does work and looks very promising.
Notes
- Looks like all your integration tests must exist in the same assembly
- Source code for LTAF is available so making this a configuration option is a possibility
- All tests must exist outside of namespaces
- Prefixing your integration tests assembly with App_Code can help work around assembly loading problems as the test runner searches for WebTestClass attributes
- Remember to have your production build process eliminate the Tests folder and associated integration test assembly
January 8th, 2009
So I had written a SmtpClientWrapper for System.Net.SmtpClient because the aforementioned class implements no interface making it impossible to perform dependency injection using it (as anyone who has to unit test any sort of email functionality can attest to). Then I had the idea that during production we may want to mirror email messages sent to a debugging email address so I also made a MirrorSmtpClientDecorator. Herein lies the problem - System.Net.MailMessage does not implement ICloneable and the MirrorSmtpClientDecorator.Send(MailMessage) either needs a copy or its going adversely affect MailMessage with a side affect (by altering the MailMessage.To MailAddressCollection for a second message sent to the debugging email addresses). I explored numerous ways to copy MailMessage and eventually I resorted to some brute force extension methods (to ObjectModel.Collection<T>; ShallowCopy and Replace, suck I know) but during my research I discovered some older MemoryStream serialization I refactored and felt its worth sharing.
The problem with this approach is that the class you want to deep copy requires that it be marked with the SerializableAttribute. I've noticed that a portion of my time is spent writing hackarounds for classes in the .NET BCL and MailMessage is no exception - yup no ICloneable and no SerializableAttribute either.
public class Serializer
{
public static void Serialize(Stream target, object source)
{
var formatter = new BinaryFormatter();
formatter.Serialize(target, source);
}
public static T Deserialize<T>(Stream source) where T : class
{
var formatter = new BinaryFormatter();
var obj = (T)formatter.Deserialize(source);
return obj;
}
}
public class MemorySerializer<T> : IDisposable where T : class
{
#region Member Variables
private readonly MemoryStream _ms;
#endregion
#region Constructors
public MemorySerializer(MemoryStream ms)
{
_ms = ms;
}
public MemorySerializer()
{
_ms = new MemoryStream();
}
#endregion
#region Methods
public MemorySerializer<T> Serialize(T obj)
{
Serializer.Serialize(_ms, obj);
_ms.Position = 0;
return this;
}
public T Deserialize()
{
return Serializer.Deserialize<T>(_ms);
}
#endregion
#region Implementation of IDisposable
public void Dispose()
{
if (_ms == null)
return;
try
{
_ms.Close();
}
finally
{
_ms.Dispose();
}
}
#endregion
}
public static class ObjectUtility
{
/// <summary>
/// Creates a deep copy of an object for any object which
/// supports binary serialization.
/// </summary>
/// <typeparam name="T">Type to serialize/deserialize.</typeparam>
/// <param name="source">Object to deep copy.</param>
/// <returns>New instance of the object with all properties and
/// fields that support serialization copied.</returns>
/// <remarks>This method isn't as fast as a provided Clone method
/// but when ICloneable is not implemented on a BCL class its a
/// pretty close substitute.</remarks>
public static T DeepCopy<T>(this T source) where T : class
{
T ret;
using (var ms = new MemorySerializer<T>())
{
ret = ms.Serialize(source).Deserialize();
}
return ret;
}
}
October 23rd, 2008
Today I implemented the ConfigurationSection stuff in .NET you can use to interject your custom configuration structures into config files like web.config and app.config. I wanted to test not only my implementation, but the classes that rely on the configuration but it wasn't exactly clear how I could get ConfigurationManager to load an arbitrary configuration.
The solution is quite simple and I figured it out after a bit of trial and error. What got me was that I had a custom configuration file in a child directory of my unit test project("WebServices") and I had set the configuration file's build action to "Copy Always". I had thought it would end up in "bin\Debug" but it correctly ended up in "bin\Debug\WebServices" so that's an important thing to keep in mind.
Outsmarting ConfigurationManager also requires that you have an accompanying file (empty text file works) that the configuration applies to. So here's what the start of my [now working] test looks like:
private static RequestHandlerSection _section;
[TestFixtureSetUp]
public void Setup()
{
using (var sw =
File.CreateText(".\\WebServices\\RequestHandlerTests")
)
{
sw.Close();
}
var config = ConfigurationManager.OpenExeConfiguration(
".\\WebServices\\RequestHandlerTests"
);
_section = (RequestHandlerSection)
config.Sections[RequestHandlerSection.Name];
}
[Test]
public void WorkDamnYouTest()
{
Assert.IsNotNull(_section);
Assert.IsNotEmpty(_section.Security);
}
}
Ideally at some point in the future ConfigurationManager could get a Load(string xml) or some such so we don't have to deal with this nonsense. While I could add one with an extension method I'd like to see it get into the framework to improve unit testability.