Wednesday, December 29, 2010
TDD – But What do I Test?
FAST FAST is not actually an answer to the question, but a setup. FAST describes what makes a good test or suite of tests.
Fast – yes, the word associated with the first letter is the same and the acronym itself. Even a unit test project with thousands of tests should take no longer than a few seconds to run.
Atomic – unit tests should test one and only one piece of functionality or behavior. Atomic also means the test is not dependent on outside systems such as a web server or service, the file system, or a database. Those should all be represented by abstractions to support the F in FAST. We should be able to write and test our domain without a UI or data store.
Static – once a test is written, it should not change. This does not mean tests can never be refactored, but if you are practicing TDD, then you are setting up expectations of how the system should behave before writing any code. The implementation of that expectation may and probably will change, but the underlying test for the behavior should remain unchanged.
Thorough – unit tests should test as many conditions as the system under test will face during its lifetime. Testing just a single passing case does not really exercise the code fully or completely. It may not be possible for us to test every edge case, but we can put the code through its paces and ensure its behavior, stability, effectiveness, and graceful failing.
BEER Now on to what needs to be tested. BEER describes a set of cases or circumstances that require reflection and attention when writing tests and designing our APIs. The B in BEER does not stands for behavior - that is just a little too vague to be instructive.
Boundary Conditions – a value that exceeds the maximum or minimum allowed value. If we are testing a validation rule for a string that is required and cannot be more than fifty characters, we need to write a series of tests that address each a series of conditions - a null string, a zero-length string, a string with one character, a string with a length greater than one and less than fifty, a string with fifty characters, and a string with more than fifty characters. The same would apply to numbers or dates. This follows the T in FAST.
Existence – Not Cartesian existence, but nulls and state. We often see this when testing repositories and services. Unit testing persistence requires inserting an object into the data store (represented by an abstractions such as a repository - remember the A in FAST) and retrieving it to make sure it was correctly saved. The opposite, removing an object from the data store, requires a test to assert that it was correctly deleted. This also applies to an object’s state. We want to write tests for state transitions and verify that our objects behave appropriately in each case.
Exceptions – although we may not be able to foresee all possible conditions, we can test for certain exception conditions and ensure they are handled gracefully. Does an object in an invalid state throw an exception? Does the exception give the user an out? Does the exception provide useful information to either the user or other developer for resolving the issue?
Range Conditions – This would include things like a start date occurring before an end date or an invalid enumerator or case in a switch statement. Testing a sorting algorithm where the first element is less than the proceeding one would fall under range conditions.
Conclusion Unit testing is not just about one case or single event. It is about proving our code works in different environments and conditions. Is is also about falsifiability. When we are writing tests, focus on the negative cases and taking a defensive coding posture. What can go wrong and how will my code react? When outside forces assert pressure on the system how will it handle them?
Following these guidelines will help you achieve not just higher test coverage, but higher quality test coverage.
Wednesday, December 15, 2010
What is a Professional Redux?
One definition might be getting paid to do something. Another might be a commitment to performing at the highest level, to give your best at all times. Yet another may be exhibiting a courteous, conscientious, and generally businesslike manner in the workplace. While all of these are partially correct, there are many facets to being a true professional.
There are two parts to learning craftsmanship: knowledge and work. You must gain the knowledge of principles, patterns, practices, and heuristics that a craftsman knows, and you must also grind that knowledge into your fingers, eyes, and gut by working hard and practicing.
It requires more than just the knowledge of principles and patterns. You must sweat over it. You must practice it yourself, and watch yourself fail. You must watch others practice it and fail. You must see them stumble and retrace their steps. You must see them agonize over decisions and see the price they pay for making those decisions the wrong way.
- Robert Martin, Clean Code
A professional has specialized skills and knowledge that required independent erudition and effort on their part to attain. They engage in a process of constant evaluation and improvement. A professional makes decisions based on their dedication to the craft and not the current circumstance. The characteristic that separates the professional from the dilettante is an uncompromising commitment to excellence – doing what is required to get the job done at its highest level, even when it is inconvenient. An amateur is capable of doing some things well under the right conditions, but a professional, as a matter of course, does it well regardless of the situation.
A professional is passionate, motivated, and punctual. A professional respects the respectable, but admires the inspirational. A professional is a seeker of knowledge but also a teacher. A professional is disciplined, has the highest standards, and is engaged in the constant pursuit of un-attainable perfection. A professional is restless and never satisfied, always evaluating and re-evaluating where they’ve come and finding ways to do what they are doing better now, today, moment to moment.
I have offended God and mankind because my work did not reach the quality it should have.
- Leonardo da Vinci, artist, d. 1519, last words
Wednesday, December 8, 2010
Project Estimates, the Silent Killer
I have written about estimating in the past. That article discussed some of the inherent challenges of software project estimating. This time I want to focus on deliverables that are NOT code.
When creating a project estimate, we tend to concentrate on development time while neglecting common and important tasks that require hours and should be considered when defining an estimate. These are the areas where overages tend to occur precisely because they were overlooked.
These are just guidelines and should be modified for your specific situation.
Project setup
This includes setting up the development, QA, staging, and production environments, installing 3rd party software, getting access to client servers, VPN, remote desktop, web services etc. – minimum 8 hours.
QA time
This number should be provided after the project scope and business requirements have been establish and estimated. Ideally, this number would be provided by a qualified QA specialist.
Code Reviews
This is an invaluable step in maintaining code throughout the project - one hour per week of the project.
Meetings
This includes design meetings, internal and external, client meetings, phone calls, email conversations, etc. - minimum one hour per week of the project per team member working on the project.
Demos
You are planning on showing something to the client right? One hour per iteration.
UAT
Let’s not forget that we must have user acceptance testing. One hour per iteration.
Post Implementation Meeting
This is another invaluable tool in fine-tuning the process of constant improvement over the lifetime of the project and the developer. Half hour per iteration per team member working on the project.
Deployment
This includes creating a deployment and rollback plans, database update scripts, deployment to QA and staging environments during development, etc. - minimum 8 hours.
Post Implementation Client Support
This is support supplied to the client after a production release - minimum 2 hours.
UI/UX Design
If a project requires graphic design, then it requires a graphic designer – minimum 24 hours for design, CSS, html, images, etc.
And those are just the minimum requirements. Check out this more comprehensive list from Steve McConnell’s Software Estimation. Demystifying the Black Art
Wednesday, December 1, 2010
Visual Layout Design Help
As software developers, most of us are expected to be many things to our customers and employers: an architect, estimator, tester, technical writer, project manager, diplomat, usability expert, visual/UI designer, etc.
In an effort to pass along knowledge that may be helpful to support education in the realm of visual layout, I wanted to share this. It is a CSS layout based on the “optimal” on-screen layout of 960 pixels. It has an HTML, CSS generator and you can download templates for more than a dozen platforms (Fireworks, Photoshop, Visio, etc.)
You can check out more of the background details here.
Wednesday, November 24, 2010
Zen Testing Extension Methods
After my last Zen Testing presentation, numerous requests have come in to share the unit test extension methods I use. They are attached at the end of this post (this class is currently setup to use MSTest, but it can be easily adapted to use other test frameworks).
The intent for creating these extension methods was to make for more readable assertions in my unit tests. So rather than:
//arrangeI wanted my assertions to read like:
string expected = "Testing123";
Person person = new Person();
person.Name = expected;
//act
string result = person.Name;
//assert
Assert.AreEqual(expected, result);
//assert
result.ShouldBe(expected);
The usage.ShouldBe(easy to follow).I do want to point out one special case for testing exceptions. The normal way of testing an exception and its message looks something like this:
[TestMethod]This is a very simple example, but it is also rather verbose for what you get. Using a simple delegate reduces the amount of test code required to achieve the same result and increases the readability:
[ExpectedException(typeof(InvalidOperationException))]
public void Save_InvalidPerson_ThrowsInvalidOperationException()
{
//arrange
PersonService service = new PersonService();
try
{
//act
service.Save(new Person());
}
catch (Exception exp)
{
Assert.AreEqual("Person is invalid", exp.Message);
throw;
}
}
[TestMethod]Just include the UnitTestExtensions class in your unit test project and take it for a spin. I hope that this will help you on the path to Zen Testing.
public void Save_InvalidPerson_ThrowsInvalidOperationException()
{
//arrange
PersonService service = new PersonService();
//act
Action action = () => service.Save(new Person());
//assert
action.ShouldThrow<InvalidOperationException>("Person is invalid.");
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Envoc.Core.UnitTests.Extensions
{
public static class UnitTestExtensions
{
public static void ShouldBe<T>(this T actual, T expected)
{
ShouldBe(actual, expected, string.Empty);
}
public static void ShouldBe<T>(this T actual, T expected, string message)
{
Assert.AreEqual(expected, actual, message);
}
public static void ShouldNotBe<T>(this T actual, T expected)
{
ShouldNotBe(actual, expected, string.Empty);
}
public static void ShouldNotBe<T>(this T actual, T expected, string message)
{
Assert.AreNotEqual(expected, actual, message);
}
public static void ShouldContain(this string actual, string expected)
{
ShouldContain(actual, expected, string.Empty);
}
public static void ShouldContain(this string actual, string expected, string message)
{
Assert.IsTrue(actual.Contains(expected), message);
}
public static void ShouldNotContain(this string actual, string expected)
{
ShouldNotContain(actual, expected, string.Empty);
}
public static void ShouldNotContain(this string actual, string expected, string message)
{
Assert.IsFalse(actual.Contains(expected), message);
}
public static void ShouldBeSameAs<T>(this T actual, T expected) where T : class
{
ShouldBeSameAs(actual, expected, string.Empty);
}
public static void ShouldBeSameAs<T>(this T actual, T expected, string message) where T : class
{
Assert.AreSame(actual, expected, message);
}
public static void ShouldNotBeSameAs<T>(this T actual, T expected) where T : class
{
ShouldNotBeSameAs(actual, expected, string.Empty);
}
public static void ShouldNotBeSameAs<T>(this T actual, T expected, string message) where T : class
{
Assert.AreNotSame(actual, expected, message);
}
public static void ShouldBeEqual<T>(this T actual, T expected)
{
ShouldBeEqual(actual, expected, string.Empty);
}
public static void ShouldBeEqual<T>(this T actual, T expected, string message)
{
Assert.AreEqual(actual, expected, message);
}
public static void ShouldNotBeEqual<T>(this T actual, T expected)
{
ShouldNotBeEqual(actual, expected, string.Empty);
}
public static void ShouldNotBeEqual<T>(this T actual, T expected, string message)
{
Assert.AreNotEqual(actual, expected, message);
}
public static void ShouldBeLessThan<T>(this T actual, T expected) where T : IComparable
{
ShouldBeLessThan(actual, expected, string.Empty);
}
public static void ShouldBeLessThan<T>(this T actual, T expected, string message) where T : IComparable
{
Assert.IsTrue(actual.CompareTo(expected) < 0, message);
}
public static void ShouldBeGreaterThan<T>(this T actual, T expected) where T : IComparable
{
ShouldBeGreaterThan(actual, expected, string.Empty);
}
public static void ShouldBeGreaterThan<T>(this T actual, T expected, string message) where T : IComparable
{
Assert.IsTrue(actual.CompareTo(expected) > 0, message);
}
public static void ShouldBeNull(this object value)
{
Assert.IsNull(value);
}
public static void ShouldNotBeNull(this object value)
{
Assert.IsNotNull(value);
}
public static void ShouldThrow<T>(this Action action) where T : Exception
{
ShouldThrow<T>(action, string.Empty);
}
public static void ShouldThrow<T>(this Action action, string message) where T : Exception
{
string failMessage = string.Format("did not throw expected exception {0}.", typeof(T).Name);
try
{
action();
Assert.Fail(failMessage);
}
catch (Exception ex)
{
if (!string.IsNullOrWhiteSpace(message))
{
Assert.AreEqual(message, ex.Message);
}
Assert.AreEqual(typeof(T), ex.GetType(), failMessage);
}
}
}
}
Wednesday, November 17, 2010
History of the World Part IV
History of the World Part I - Douglas Engelbart
History of the World Part II – Ada Lovelace
History of the World Part III – H. Edward Roberts
This time I want to talk about one of the most misunderstood, over-adopted methodologies in software development, and the man who inadvertently introduced us to it – Winston Royce.
Dr. Winston W. Royce (1929–1995) was a computer scientist, director at Lockheed Software Technology Center in Austin, Texas, and the man who described the Waterfall model for software development.
Agile, SCRUM, and XP were a response to the failures of traditional software design and development methodology commonly referred to as waterfall. How the waterfall methodology came to be used as the central software development method used by most companies is an interesting tale. In the 1970′s, the DOD (Department of Defense) was researching methods to handle software development to better report on where money and resources were being spent. A member of the team assigned to this task went to the Institute of Electrical and Electronics Engineers (IEEE), to look for articles on the subject. He found an article named “Managing the Development of Large Software Systems: Concepts and Techniques”. He opened the first diagram – a graph representing the waterfall method – titled “Implementation steps to develop a large computer program for delivery to a customer”. It was from this that the DOD established the Military Standard 2167 which used the waterfall graph as a template – without actually having read the article.
All the vendors that supported the DOD had to accept the new standard of developing software. NATO was also looking for a way to work with software and found the DOD had done a research project on the subject and they too adopted waterfall. Soon, the software industry also followed suit and before you knew it, everyone was working with this new method, called the Waterfall method.
Had they taken the time to actually read the article, they would have found that Dr. Royce goes into detail about why the waterfall method does not work. Namely, waterfall has testing and documentation at the end of the project. It assumes that all the preceding stages are accurate. It also assumes that the estimate of work is exact. We know, however, that when the schedule starts to slip, those things at the end of the project are done hastily or dropped. What’s at the end of the project? Testing and documentation.
For the next several decades the DOD recorded a 60-90% failure rate on software development projects. Eventually, they rescinded the Military Standard 2167.
For the rest of his life, Dr. Royce fought to clear his name. Below is a video that nicely illustrates what happened. Enjoy.
Tuesday, November 16, 2010
Upcoming Speaking Engagements
Tales from the Code
3:30-4:30pm
Fayard Hall Room 218
Southeastern University Campus
Wednesday, November 10, 2010
History of the World Part III
History of the World Part I - Douglas Engelbart
History of the World Part II – Ada Lovelace
Today we honor Henry Edward "Ed" Roberts (September 13, 1941 – April 1, 2010), commonly referred to as "the father of the personal computer". He co-founded Micro Instrumentation and Telemetry Systems (MITS) in Albuquerque in 1969 and served as its president. The company made and sold rocket kits and then calculators, but their true contribution was the Altair 8800 personal computer that used the new Intel 8080 microprocessor. It was featured on the cover of the January 1975 issue of Popular Electronics, and hobbyists flooded MITS with orders for this $397 computer kit. It so inspired Bill Gates and Paul Allen that they moved to Albuquerque and joined MITS to develop software and Altair BASIC, Microsoft's first product. Roberts sold MITS in 1977 and retired to Georgia where he farmed, studied medicine, and eventually became a small-town doctor. He died on April 1, 2010 of pneumonia, but the effect of his contribution to our industry cannot be measured.
He may not have invented the PC, but he surely invented the PC industry.
Monday, November 8, 2010
Upcoming Speaking Engagements
Zen Coding
12:00-1:00pm
Fayard Hall Room 205
Southeastern University Campus
More Info
Wednesday, November 3, 2010
History of the World Part II
In the first article of this series, we talked about why learning about our history as developers is an important endeavor, and introduced Douglas Engelbart.
This time we will talk about the person attributed with the distinction of being the first software developer, Ada Lovelace.
Ada Lovelace wrote a paper in 1843 that anticipated the development of computer software, artificial intelligence, and computer music. Daughter of the poet Lord Byron, Ada Lovelace was known as the "enchantress of numbers" who collaborated with Charles Babbage, the inventor of the first mechanical thinking/calculating machine.
At the age of 17, Ada was introduced to Mary Somerville. It was at a dinner party at Mrs. Somerville's in November 1834 that Ada first heard Babbage's ideas for a new calculating machine, the Analytical Engine. What if a calculating engine could not only foresee but could act on that foresight?
Babbage worked on plans for this new engine and reported on the developments at a seminar in Turin, Italy in the fall of 1841. Luigi Menabrea wrote a summary of what Babbage described and published an article about the development. Ada translated Menabrea’s memoir, appending her own notes, including a method for calculating Bernoulli numbers with the machine – and is attributed as the first computer program.
Understanding that computers could do a lot more than just crunch numbers, Ada suggested that the Analytical Engine “might compose elaborate and scientific pieces of music of any degree of complexity or extent”, produce graphics, and would be used for both practical and scientific use.
She never had the chance to fully explore the possibilities of either Babbage’s inventions or her own understanding of computing. She died at age 36 on 27th November 1852 of cancer.
Her contribution to our profession is so significant, in 1979 the U.S. Department of Defense developed a computer language named "Ada" in her honor, March 24th is recognized as “Ada Lovelace Day”, and a movie about her life called “Enchantress of Numbers” is slated for release in 2011.
Wednesday, October 27, 2010
Upcoming Speaking Engagements
Thursday October 28th, 2010
SELU .NET User GroupZen Testing
6:30-8:00pm
Fayard Hall Room 126
Southeastern University Campus
More Info
Wednesday, October 20, 2010
History of the World Part I
As software practitioners, we are in pursuit of two things: the acquisition of skill, and the acquisition of knowledge. It is the acquisition of the latter that I want to focus on in the next series of articles – specifically history.
Many developers are deficient in the knowledge or concern of how our profession was born and how it has evolved, or why that information matters. This is, in part, due to the fact that we live in the present, and plan for the future. But, historical perspective provides us with insight that cannot be derived from the present. Studying our history provides reason and basis for the ideas, principles, techniques, and practices that have shaped this industry and craft. It also provides the context in which these concepts were incubated, developed, extended, and, in some cases, abandoned.
In this installment, I would like to introduce you to Douglas Engelbart. He, among other things, invented one of the most ubiquitous computer peripherals, the mouse. He and his team also introduced the world to hypertext and computer networking. Among his other contributions are: collaborative hypermedia, knowledge management, community networking, and organizational transformation, display editing, windows, cross-file editing, outline processing, hypermedia, and groupware. Mr. Engelbart’s biography is truly impressive, inspirational, and worth taking a few minutes to read.
On December 9, 1968, at the Fall Joint Computer Conference (FJCC) in San Francisco, he demonstrated the first computer mouse, as well as interactive text, video conferencing, teleconferencing, email, hypertext and a collaborative real-time editor. This demonstration has been posthumously named "The Mother of all Demos". The original 100-minute video of this event is part of the Engelbart Collection in Special Collections of Stanford University and can be viewed here. Enjoy.
Wednesday, October 13, 2010
The Myth of User Error
Have you ever had the “sounds like user error” knee-jerk response without taking the time to find the real cause of the problem? Have you ever heard a colleague do likewise? The concept of user error is so prevalent, we have created our own set of slang - “Problem exists between keyboard and chair”, “Problem in chair not in computer”, “ID-10T error”, etc. But is the user really to blame? I say the answer is no.
I once inherited a codebase where all lookup lists were implemented as Singletons. When the administrator went to add a new lookup value, it never showed up in the web form that displayed the list, so she re-booted her machine and then it “magically” appeared the next time she fired up the application. She blamed herself for the issue. She figured it was something she had done wrong, not that it was in fact an issue with the code.
Every problem a user encounters while using your application was created by you, the developer. You allowed it to happen. You put the user in a position to, or gave them the opportunity to fail. Whether it be from a lack of ability, QA, testing, or education, domain ignorance, laziness, or just a simple oversight, you are to blame. If you do get burnt, do not take it personally. Take responsibility and view it as an opportunity to learn and grow as a professional.
Wednesday, October 6, 2010
Upcoming Speaking Engagements
Saturday October 9th, 2010
Houston TechFest
Zen Coding
12:00-1:15pm
University of Houston Campus
Wednesday, September 29, 2010
Fluent Validation Part III – The Repository
This is the fourth installment in this series:
- Introduction – The basics or what Fluent Validation is
- Part I – TDD with Fluent Validation and string validation
- Part II – Date validation and custom property validators
This time we will take a look at how we can take advantage of a repository to help us with our data validation. Using a repository helps separate our data access concern from our model, allows our architecture to be more flexible, and maximizes the amount of testable code by abstracting the data layer.
We will continue to build upon the project we have been using from the beginning, so let’s get started by firing up Visual Studio, and opening our FluentValidation.Example.Solution. Open the Employee class and add a new property SocialSecurityNumber:
public class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string SocialSecurityNumber { get; set; }
}
public interface IEmployeeRepository
{
IQueryable<Employee> Select();
void Save(Employee item);
void Delete(Employee item);
bool IsUnique(Employee item);
}
public class FakeEmployeeRepository : IEmployeeRepository
{
private List<Employee> data;
public FakeEmployeeRepository()
{
LoadData();
}
private void LoadData()
{
data = new List<Employee>
{
new Employee{ ID=1, SocialSecurityNumber = "123-45-6789" }
};
}
public IQueryable<Employee> Select()
{
return data.AsQueryable();
}
public void Save(Employee item)
{
throw new NotImplementedException();
}
public void Delete(Employee item)
{
throw new NotImplementedException();
}
public bool IsUnique(Employee item)
{
return !data.Where(HasMatchingSocialSecurityNumber(item)).Any();
}
private Func<Employee, bool> HasMatchingSocialSecurityNumber(Employee item)
{
return x => !x.ID.Equals(item.ID) && x.SocialSecurityNumber.Equals(item.SocialSecurityNumber);
}
}
[TestInitialize]
public void Init()
{
//arrange
target = new Employee();
validator = new EmployeeValidator(new FakeEmployeeRepository());
}
public EmployeeValidator(IEmployeeRepository repository)
{
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("First name is required.")
.Length(0, 50)
.WithMessage("First name cannot exceed 50 characters.")
.Matches(@"^[A-Za-z\-\.\s]+$")
.WithMessage("First name contains invalid characters.");
}
[TestMethod]
public void SocialSecurityNumber_NonUniqueValue_ValidationFails()
{
//arrange
target.SocialSecurityNumber = "123-45-6789";
//act
ValidationResult result = validator.Validate(target, x => x.SocialSecurityNumber);
//assert
validator.ContainsRuleFor<Employee>(x => x.SocialSecurityNumber);
result.AssertValidationFails("Social security number must be unique.");
}
[TestMethod]
public void SocialSecurityNumber_UniqueValue_ValidationPasses()
{
//arrange
target.SocialSecurityNumber = "987-65-4321";
//act
ValidationResult result = validator.Validate(target, x => x.SocialSecurityNumber);
//assert
validator.ContainsRuleFor<Employee>(x => x.SocialSecurityNumber);
result.AssertValidationPasses();
}
public class EmployeeValidator : AbstractValidator<Employee>
{
private IEmployeeRepository repository;
public EmployeeValidator(IEmployeeRepository repository)
{
this.repository = repository;
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("First name is required.")
.Length(0, 50)
.WithMessage("First name cannot exceed 50 characters.")
.Matches(@"^[A-Za-z\-\.\s]+$")
.WithMessage("First name contains invalid characters.");
RuleFor(x => x.SocialSecurityNumber)
.Must(BeAUniqueSocialSecurityNumber)
.WithMessage("Social security number must be unique.");
}
private bool BeAUniqueSocialSecurityNumber(Employee item, string socialSecurityNumber)
{
return repository.IsUnique(item);
}
I hope you have enjoyed this series and learned some of the techniques available for simple and complex validation, business rules, and how to unit test them. If you have any questions or if I’ve left any loose ends, feel free to contact me. Thank you and happy coding!
Wednesday, September 22, 2010
Fluent Validation Part II – Dates
This is the third installment in this series:
- Introduction – The basics or what Fluent Validation is
- Part I – TDD with Fluent Validation and string validation
This time we will take a look at date validation. We will build upon the project we have been using from the beginning, so let’s get started by firing up Visual Studio, and opening our FluentValidation.Example.Solution. Add a new class to our Models project called Contract:
public class Contract
{
public DateTime ExecutionDate { get; set; }
public DateTime? ExpirationDate { get; set; }
}
In this example, our business requirements state a contract’s execution date must be before and contract’s expiration date (notice the expiration date is nullable) and both dates must be valid SQL dates - the year must be greater than 1753.
Note: If you have been following along in this series, there is a slight change that needs to be made to the ValidatorExtensions class to allow for complex type properties. Replace the existing ContainsRuleFor<T> method with the following:
public static void ContainsRuleFor<T>(this IValidator validator, Expression<Func<T, object>> propertyExpression)
{
string propertyToTest = ConvertExpressionToString(propertyExpression);
ContainsRuleFor(validator, propertyToTest);
}
private static string ConvertExpressionToString<T>(Expression<Func<T, object>> propertyExpression)
{
if (typeof(UnaryExpression).Equals(propertyExpression.Body.GetType()))
{
var operand = ((UnaryExpression)propertyExpression.Body).Operand;
return ((MemberExpression)operand).Member.Name;
}
return ((MemberExpression)propertyExpression.Body).Member.Name;
}
With that update in place, and our business rules in mind, it is time to write our first test. Add a new class to the UnitTests project called ContractValidatorTests.
[TestClass]
public class ContractValidatorTests
{
private Contract target;
private IValidator<Contract> validator;
[TestInitialize]
public void Init()
{
//arrange
target = new Contract();
validator = new ContractValidator();
}
[TestMethod]
public void ExecutionDate_LessThanJanuary11753_ValidationFails()
{
//act
ValidationResult result = validator.Validate(target);
//assert
validator.ContainsRuleFor<Contract>(x => x.ExecutionDate);
result.AssertValidationFails("Execution date is invalid.");
}
At this point, our code will not compile until we’ve added a ContractValidator class. Add a new class name ContractValidator to the Models project.
public class ContractValidator : AbstractValidator<Contract>
{
}
Our test will compile and fail as expected – so far, so good.
Now, let’s implement our first validation rule and get that test to pass. Add the following to the ContractValidator class:
public ContractValidator()
{
RuleFor(x => x.ExecutionDate)
.Must(BeAValidSqlDate)
.WithMessage("Execution date is invalid.");
}
private bool BeAValidSqlDate(DateTime date)
{
if (ReferenceEquals(date, null))
{
return true;
}
return date.Year > 1753;
}
This time we’ve introduced the Must() predicate validator. There are several ways to implement this method, but in this case, we have chosen to delegate the logic to the BeAValidSqlDate method (this may be an unusual name for a method, but it reads better in context). Let’s run our test again and it will pass. This method will only work for execution date, not expiration date as it is a nullable type. We’ll fix that shortly.
I am going to add all of our tests now, and then walk through adding the validation rules.
[TestMethod]
public void ExecutionDate_Year1753_ValidationFails()
{
//arrange
target.ExecutionDate = DateTime.Parse("1/1/1753");
//act
ValidationResult result = validator.Validate(target, x => x.ExecutionDate);
//assert
validator.ContainsRuleFor<Contract>(x => x.ExecutionDate);
result.AssertValidationFails("Execution date is invalid.");
}
[TestMethod]
public void ExecutionDate_YearGreaterThan1753_ValidationPasses()
{
//arrange
target.ExecutionDate = DateTime.Parse("1/1/1754");
//act
ValidationResult result = validator.Validate(target, x => x.ExecutionDate);
//assert
validator.ContainsRuleFor<Contract>(x => x.ExecutionDate);
result.AssertValidationPasses();
}
[TestMethod]
public void ExecutionDate_GreaterThanExpirationDate_ValidationFails()
{
//arrange
target.ExecutionDate = DateTime.Today;
target.ExpirationDate = DateTime.Today.AddDays(-1);
//act
ValidationResult result = validator.Validate(target, x => x.ExecutionDate);
//assert
validator.ContainsRuleFor<Contract>(x => x.ExecutionDate);
result.AssertValidationFails("Execution date must be before expiration date.");
}
If we run our tests now, they will fail, again following the TDD method of red-green-refactor.
We could update our rules like this:
RuleFor(x => x.ExecutionDate)
.Must(BeAValidSqlDate)
.WithMessage("Execution date is invalid.")
.LessThan(x => x.ExpirationDate.Value)
.WithMessage("Execution date must be before expiration date.");
and ordinarily our tests would pass. Give it a try. Our tests are still failing. What happened? This is because of the nullable expiration date. In order to get our tests to pass, we must create a custom rule to accommodate the nullable expiration date. Update the ContractValidator so it looks like this:
public ContractValidator()
{
RuleFor(x => x.ExecutionDate)
.Must(BeAValidSqlDate)
.WithMessage("Execution date is invalid.");
Custom(x =>
{
if (ExecutionDateGreaterThanExpirationDate(x))
{
return new ValidationFailure("ExecutionDate", "Execution date must be before expiration date.");
}
return null;
});
}
private bool ExecutionDateGreaterThanExpirationDate(Contract contract)
{
if (ReferenceEquals(contract.ExpirationDate, null))
{
return false;
}
return contract.ExecutionDate > contract.ExpirationDate.Value;
}
private bool BeAValidSqlDate(DateTime date)
{
if (ReferenceEquals(date, null))
{
return true;
}
return date.Year > 1753;
}
With these updated in place, run your tests again. Now they all pass.
Remember earlier I said we would fix the BeAValidSqlDate method so it works with nullable DateTime properties? In order to do that, we need to make another change to the code. First, we will create a custom property validator. Add a new class to the Models project called SqlDateValidator:
public class SqlDateValidator : PropertyValidator
{
public SqlDateValidator()
: base("Year must be greater than 1753")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
DateTime? date = context.PropertyValue as DateTime?;
if (ReferenceEquals(date, null))
{
return true;
}
return date.Value.Year > 1753;
}
}
Notice that this class extends the PropertyValidator base class. Now let’s add a couple of tests for the expiration date and update our ContractValidator class:
[TestMethod]
public void ExpirationDate_Year1753_ValidationFails()
{
//arrange
target.ExpirationDate = DateTime.Parse("1/1/1753");
//act
ValidationResult result = validator.Validate(target, x => x.ExpirationDate);
//assert
validator.ContainsRuleFor<Contract>(x => x.ExpirationDate);
result.AssertValidationFails("Expiration date is invalid.");
}
[TestMethod]
public void ExpirationDate_YearGreaterThan1753_ValidationPasses()
{
//arrange
target.ExpirationDate = DateTime.Parse("1/1/1754");
//act
ValidationResult result = validator.Validate(target, x => x.ExpirationDate);
//assert
validator.ContainsRuleFor<Contract>(x => x.ExpirationDate);
result.AssertValidationPasses();
}
[TestMethod]
public void ExpirationDate_IsNull_ValidationPasses()
{
//act
ValidationResult result = validator.Validate(target, x => x.ExpirationDate);
//assert
validator.ContainsRuleFor<Contract>(x => x.ExpirationDate);
result.AssertValidationPasses();
}
Run the tests to verify they fail. Now for update the ContractValidator:
public ContractValidator()
{
RuleFor(x => x.ExecutionDate)
.SetValidator(new SqlDateValidator())
.WithMessage("Execution date is invalid.");
Custom(x =>
{
if (ExecutionDateGreaterThanExpirationDate(x))
{
return new ValidationFailure("ExecutionDate", "Execution date must be before expiration date.");
}
return null;
});
RuleFor(x => x.ExpirationDate)
.SetValidator(new SqlDateValidator())
.WithMessage("Expiration date is invalid.");
}
private bool ExecutionDateGreaterThanExpirationDate(Contract contract)
{
if (ReferenceEquals(contract.ExpirationDate, null))
{
return false;
}
return contract.ExecutionDate > contract.ExpirationDate.Value;
}
We’ve simplified our ContractValidator by using the SqlDateValidator class, which accommodates nullable and non-nullable DateTime properties. We’ve also created a reusable property validator. Now all of our tests will pass.
One thing to remember about the FluentValidation library, there are many ways to achieve the same result, some are trickier than others. Making sure we have unit tests to prove our validation works allows us to try different methods and refactor the code to find the best solution without fear of breaking existing functionality.
Next time we will take a look at validation using a repository.
Thursday, September 16, 2010
Upcoming Speaking Engagements
Thursday September 16th, 2010
SELU .NET User GroupZen Coding
6:30-8:30pm
Fayard Hall Room 207
Southeastern University Campus
More Info
Wednesday September 22th, 2010
AITP LSU ChapterZen Coding
Patrick F. Taylor Hall Room 2174
Louisiana State University
Baton Rouge, LA 70803-6316
6:00-7:15pm
More Info
Wednesday, September 15, 2010
Fluent Validation – Part I
In the introduction article of this series, we looked at a very basic example of how to use the Fluent Validation framework. Moving forward, we will be following a Test driven development (TDD) approach to creating and implementing our validation rules and classes (I am assuming you some have knowledge in this area, as this series is not about TDD. There are plenty of great resources on the web for this.)
So let’s get started by firing up Visual Studio, and creating a blank solution called FluentValidation.Example.Solution. We will add two projects to our solution, FluentValidation.Example.Models, and FluentValidation.Example.UnitTests (for unit testing I will be using the MSTest tool, but feel free to use NUnit or any other testing framework). Also, add a reference to the Models project in our UnitTests project and a reference to the FluentValidation.dll in our UnitTests and Modes projects (this can be downloaded here. I am using the 1.3 version).
Add a new class to your UnitTests project called ValidatorExtensions (you may need to add using statements for FluentValidation.Validators and FluentValidation.Results to get this code to compile):
public static class ValidatorExtensions
{
public static void ContainsRuleFor<T>(this IValidator validator, Expression<Func<T, object>> propertyExpression)
{
string propertyToTest = ((MemberExpression)propertyExpression.Body).Member.Name;
ContainsRuleFor(validator, propertyToTest);
}
public static void ContainsRuleFor(this IValidator validator, string propertyToTest)
{
var descriptor = validator.CreateDescriptor();
var validationRules = descriptor.GetValidatorsForMember(propertyToTest);
List<IPropertyValidator> listToTest = new List<IPropertyValidator>(validationRules);
Assert.IsTrue(listToTest.Count > 0, "No validation rules have been defined for " + propertyToTest);
}
public static void AssertValidationFails(this ValidationResult results, string errorMessage)
{
Assert.IsFalse(results.IsValid, "Object is valid");
Assert.IsTrue(results.Errors.Count > 0, "No validation errors were reported");
Assert.AreEqual(errorMessage, results.Errors[0].ErrorMessage);
}
public static void AssertValidationPasses(this ValidationResult results)
{
Assert.IsTrue(results.IsValid, "Object is not valid");
Assert.AreEqual(0, results.Errors.Count, "Validation errors were reported");
}
}
This may look like a lot of code, and you may not understand some of it right now. That’s ok. As we proceed, it will start to make more sense. Our first two extension methods are called ContainsRuleFor. These methods interrogate our validator class looking to see if any validation rules exist for a given property. This is important, because our unit tests will incorrectly pass when no rules exist, which makes sense. If there is no rule to break, the test passes. Since we are going to follow a test-driven approach, we want our tests to fail until we have implemented the validation rules and these methods help facilitate that behavior. Our next two extension methods, AssertValidationFails and AssertValidationPasses test the results of our validation and check to see if the object is valid when it should be. For this example, we are going to create a simple Employee class and add some apply some validation rules to it. Add a new class to the Models project called Employee:
public class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Our business requirements state that an employee must have a first and last name, and the maximum number of characters is fifty. We also don’t want any numbers or special characters in our names so we will limit the input to the ABCs, a dash, a space, and a period. With these rules in mind, it is time to write our first test. Add a new class to the UnitTests project called EmployeeValidatorTests.
[TestClass]
public class EmployeeValidatorTests
{
private Employee target;
private IValidator<Employee> validator;
[TestInitialize]
public void Init()
{
//arrange
target = new Employee();
validator = new EmployeeValidator();
}
[TestMethod]
public void FirstName_OnInitialize_ValidationFails()
{
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationFails("First name is required.");
}
}
At this point, our code will not compile until we’ve added a few things. Add using statements for FluentValidation, FluentValidation.Results, and to our Models project namespace. Next we need to create an EmployeeValidator class. Add a new class name EmployeeValidator to the Models project.
public class EmployeeValidator : AbstractValidator<Employee>
{
}
Notice that it extends the AbstractValidator base class provided by the FluentValidation framework. This is how all of our validation classes will begin. If we run this test now, it will fail – which is the result we are looking for (this is in line with the TDD mantra “red-green-refactor”). Now, let’s implement our first validation rule. Add the following to the EmployeeValidator class:
public EmployeeValidator()
{
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("First name is required.");
}
We’ve added a constructor and defined our first validation rule for the FirstName property – it must not be empty or a value is required (the Fluent Validation framework defines “empty” as null or an empty string). Run the test again. Our test passed! We are one step closer to coding Nirvana. Using a TDD approach, we would normally follow a “red-green-refactor” process of writing a failing test, then writing enough code to get the test to pass, then refactor the code to production quality. I am going to skip ahead slightly and just include all of the remaining tests for the FirstName property:
[TestMethod]
public void FirstName_EmptyString_ValidationFails()
{
//arrange
target.FirstName = string.Empty;
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationFails("First name is required.");
}
[TestMethod]
public void FirstName_Null_ValidationFails()
{
//arrange
target.FirstName = null;
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationFails("First name is required.");
}
[TestMethod]
public void FirstName_GreaterThan50Characters_ValidationFails()
{
//arrange
target.FirstName = "".PadRight(51, 'X');
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationFails("First name cannot exceed 50 characters.");
}
[TestMethod]
public void FirstName_StartsWithANumericValue_ValidationFails()
{
//arrange
target.FirstName = "24Name";
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationFails("First name contains invalid characters.");
}
[TestMethod]
public void FirstName_ContainsANumericValue_ValidationFails()
{
//arrange
target.FirstName = "Name24Number";
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationFails("First name contains invalid characters.");
}
[TestMethod]
public void FirstName_EndsWithANumericValue_ValidationFails()
{
//arrange
target.FirstName = "Name24";
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationFails("First name contains invalid characters.");
}
[TestMethod]
public void FirstName_IsANumericValue_ValidationFails()
{
//arrange
target.FirstName = "254";
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationFails("First name contains invalid characters.");
}
[TestMethod]
public void FirstName_NotEmptyLessThan50CharactersWithNoNumericValues_ValidationPasses()
{
//arrange
target.FirstName = "Jack";
//act
ValidationResult result = validator.Validate(target, x => x.FirstName);
//assert
validator.ContainsRuleFor<Employee>(x => x.FirstName);
result.AssertValidationPasses();
}
Some of these tests will fail because we have not updated our validator to include the rules for string length and RegEx validation. Update the EmployeeValidator class constructor so it looks like this:
public EmployeeValidator()
{
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("First name is required.")
.Length(0, 50)
.WithMessage("First name cannot exceed 50 characters.")
.Matches(@"^[A-Za-z\-\.\s]+$")
.WithMessage("First name contains invalid characters.");
}
Our validation rules have now been extended to include a range validation for our string (0 to 50 characters), and a RegEx validation for letters, a dash, a period, and a space. You may be wondering why I chose to set the minimum length to 0 when our requirements state that the FirstName property is required. Well, the NotEmpty rule covers that, and I only want to show the user what state requires their attention. If the Length validation was set to Length(1, 50) and the user had not provided a value for FirstName, the validation result would show two error messages: “First name is required”, “First name cannot exceed 50 characters”. The first message makes sense, but the second doesn’t apply since the user has yet to supply a value. After that lengthy explanation, run the tests again and they should all pass. Whew! We’ve covered a lot of ground this time. You've seen how to use FluentValidation with Test-Driven Development, and we’ve created a few extension methods to help us. We’ve done some pretty extensive testing of our business rules, and you can see how to do some really powerful string validation including limiting the number of characters and using RegEx. As an exercise for the reader, implement the tests for the LastName property. Next time we will look at validation for other data types and how to create custom validation rules.
Wednesday, September 8, 2010
Fluent Validation - Introduction
I have been using the Fluent Validation library for a few years and wanted to share some of the tricks I have learned. This will be broken into a series of posts that will gradually introduce more complex functionality and examples.
So what is “fluent validation”? From their website:
A small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules for your business objects.
And it does this very well. To get started, download the latest version (the examples here are using the 1.3 version).
In this post, I am just going to show you the mechanics of creating a validator class and how to use it. For this example, we will have two classes, Employee and EmployeeValidator.
public class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class EmployeeValidator : AbstractValidator<Employee>
{
public EmployeeValidator()
{
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("First name is required.");
RuleFor(x => x.LastName)
.NotEmpty()
.WithMessage("Last name is required.");
}
}
Our Employee class is pretty basic so let’s spend some time examining our validator class. First, notice that it extends the AbstractValidator base class from the FluentValidator library (you may need to add a reference to the FluentValidation.dll you downloaded earlier). Next, we define a set of business rules in the constructor. In this case our business rules are:
- First name cannot be null or empty
- Last name cannot be null or empty
To define a rule, we use the RuleFor method that uses a Lambda expression to define what property the rule will apply to. There are several built-in validators for strings, numbers, predicates, RegEx, even an email validator (you can also create custom validators which I will get to later in the series).
Now that we have a validator, the usage is quite simple. We pass our object to our validator through the Validate method and receive a ValidationResult:
class Program
{
static void Main(string[] args)
{
Employee employee = new Employee();
IValidator validator = new EmployeeValidator();
ValidationResult result = validator.Validate(employee);
ShowResult(result);
employee.FirstName = "Jack";
employee.LastName = "Handy";
result = validator.Validate(employee);
ShowResult(result);
}
static void ShowResult(ValidationResult result)
{
if (result.IsValid)
{
Console.WriteLine("Employee is valid.");
}
else
{
Console.WriteLine("Employee is NOT valid.");
List<ValidationFailure> errors = new List<ValidationFailure>(result.Errors);
errors.ForEach(x => Console.WriteLine(x.ErrorMessage));
}
}
}
According to our validation rules, when we instantiate a new Employee object, it will be invalid because the FirstName and LastName properties will be empty. The first time we call ShowResult, our employee object will be invalid. We iterate over the Errors collection of the ValidationResult object and write the error messages to the console window. The second time we call ShowResult, the FirstName and LastName properties have been set, and our object is valid.
Running the above code will result in:
Hopefully this very simple example gives you an idea of how this might be used in your domain. Next time we will take a TDD approach to writing validation rules and look at some more complex examples.