Home > Uncategorized > validation-specification-testing

validation-specification-testing

Validation specification testing for c# domain entities

I have just been revisiting fluent configuration in the context of writing a fluent tester for validation of my domain entities. In that post, I wrote my test assertions as:

   ValidMO.ImageBanner.ShouldNotBeNull();
   ValidMO.ImageBanner.IsValid().ShouldBeTrue();
   new ImageBanner().SetupWithDefaultValuesFrom(ValidMO.ImageBanner).IsValid().ShouldBeTrue();
   new ImageBanner { Name = "" }.SetupWithDefaultValuesFrom(ValidMO.ImageBanner).IsValid().ShouldBeFalse();

This is okay and I have had luck teaching it. But, it feels long-winded for what I really want to do:

  • check that my fake is valid
  • check validations for each property (valid and invalid)

Today, I was looking at Fluent Nhibernate and noticed their persistence specification testing. This looks much more expressive:

	[Test]
	public void CanCorrectlyMapEmployee()
	{
	    new PersistenceSpecification<Employee>(session)
	        .CheckProperty(c => c.Id, 1)
	        .CheckProperty(c => c.FirstName, "John")
	        .CheckProperty(c => c.LastName, "Doe")
	        .VerifyTheMappings();
	}

How about this then for a base test:

	Test.ValidationSpecification<ImageBanner>(ValidMO.ImageBanner)
		.CheckPropertyInvalid(c => c.Name, "")
		.Verify();

This test makes some assumptions:

  • It will call IsValid() method on the entity
  • It allows you to pass in a value to the property, in this case an empty string
  • It will need to make assertion with your current test framework (fine, we do that in storyq)

You can see that I prefer the static constructor rather than using new.

There are obviously, a range of syntax changes I could make here that would mimic the validation attributes. For example:

	Test.ValidationSpecification<ImageBanner>(ValidMO.ImageBanner)
		.CheckPropertyMandatory(c => c.Name)
		.CheckPropertyAlphaNumeric(c => c.Name)
		.Verify();

Because the .CheckProperty would be extension method, you could easily add then as you go for your validations. Let’s start with that because that’s all I need for now – we will want to be able to change the callable IsValid method. Fluent nhibernate also passes in a IEqualityComparer that makes me wonder if a mechanism like this could be useful – it certainly looks cool!

There are still problems with this. The code is not DRY c => c.Name. This is because the test is focussed around a property rather than a mapping. So the above syntax would be useful when the test’s unit of work (business rule) is about combining properties. I think then that we would need another expression when want to express multiple states on the same property. Let’s give that a go:

	Test.ValidationSpecification<ImageBanner>(ValidMO.ImageBanner)
		.WithProperty(c => c.Name)
		.CheckMandatory()
		.CheckAlphaNumeric()
		.Verify();

I find the language still a little too verbose, so I might start making it smaller to:

	Test.ValidationSpecification<ImageBanner>(ValidMO.ImageBanner)
		.With(c => c.Name)
		.IsMandatory()
		.IsAlphaNumeric()
		.Verify();

I look at this code and wonder if I have a lured by the fluent syntax that doesn’t work for testing properties (rather than its original purpose to test mappings). I will have to provide an implentation of both IsAlphaNumeric() and IsMandatory(). And then I am left with the question of how to mix the positive and negative assertion on Verify().

Here, I’m not sure that I am any better off when it comes to typing less. I am typing more and I have yet another little DSL to learn. I do think though that if I am writing business software which requires clarity in the domain model this is going to be useful. I can do a few things:

One, I can write my domain rules test-first. Looking at the example above there are a couple of things that can help me test first. When typing the c => c.Name I am going to get IDE support – well, I am in Visual Studio with Resharper. I can type my new property and get autocompletion of the original object. Because I am going to specify the value in the context of the test eg c=> c.Name, "My new name" it is good that I don’t have to go near the original object. Furthermore, it is okay because I am not going to have the overhead of having to move back to my MotherObject class to create the data on the new object. I may do this later but will do so as a refactor. For example, I now realise that the domain object with the name “My new name” is somehow a canonical form I would create a new mother object for use in other tests eg ValidMO.WithNewName. Here’s what this verbose code looks like.

	public static ImageBanner WithNewName {
		get 
		{
			var mo = ValidMO.ImageBanner;
			mo.name = "";
			return mo
		}
	}

Two, I can understand when I have extended my set of rules. With this approach, I have to use the abstraction layer in the fluent interface object (eg IsMandatory(), IsAlphaNumeric()). When I haven’t got an implementation then I am not going to do test first because the call simply isn’t there. I am of the opinion that this is for the best because the barrier to entry to entry of creating new validation is higher. This may seem counterintuitive. When writing business software, I always have developers with less experience (to no experience) in writing test-first, domain objects. Few of them have either used a specification pattern or validations library. I therefore need to ensure that when implementing a new validation type (or rule) that they are have intensely understood the domain and the existing validations and ensure that it does not already exist. Often rules types are there and easy to miss; other times, there is an explosion of rule types because specific uses of a generalised rule has been created as a type – so a little refactor is better. So, the benefit of having to slow and implement a new call in the fluent interface object is one that pushes us to think harder about delaying rather than rushing.

Three, I should be able to review my tests at a different level of granularity. By granularity, I think I probably mean more a different grouping. Often on a property, there are a number of business rules in play. In the example, name let’s just imagine that I had correct named it firstName – this set of tests is about the first name by itself. There is then another rule and that is how it combines with lastName because the business rule is that the two of them make up the fullname. The next rule is to do with this combination and that is say, that the two names for some reason cannot be the same. I wouldn’t want to have that rule in the same test because that creates a test smell. Alternatively, I might have another rule about the length of the field. This rule is imposed because we are now having an interaction with a third party which requires this constraint. This would be easy then to create new rules that allows for a different grouping. I re-read this and the example seem benign!

Let’s come back to the original syntax and compare the two and see if we have an easier test:

new ImageBanner { Name = "" }.SetupWithDefaultValuesFrom(ValidMO.ImageBanner).IsValid().ShouldBeFalse();

Is this any better?

	Test.ValidationSpecification<ImageBanner>(ValidMO.ImageBanner)
		.With(c => c.Name, "")
		.VerifyFails();

As I read this, I can see that everyone is going to want there own nomenclature which will lead to rolling there own set of test helpers. Clearly, both are using a functional sytle of programming that was easier to do since C# 3.5. However, the top example chains different library syntax together to make it work:

  • new ImageBanner { Name = "" } – C# 3.5 object initialiser
  • .SetupWithDefaultValuesFrom(ValidMO.ImageBanner) – custom helper
  • .IsValid() – domain object’s own self checking mechanism
  • .ShouldBeFalse();BDD helper

The bottom example, provides an abstraction across all of these to that only uses the C# 3.5 syntax (Generics and lambas).

  • Test.ValidationSpecification<ImageBanner>(ValidMO.ImageBanner) – returns a object that contains the object setup through the validMO
  • .With(c => c.Name, "") – allows you access back to your object to override values
  • .VerifyFails() – wraps the IsValid() and ShouldBeFalse))

In conclusion, I think it might work. Put differently, I think the effort may be worth it and pay dividends. I don’t think that it will distract from the Mother object strategy which I find invaluable in teaching people to keep data/canonical forms separate from logic and tests.

In reflection, there is one major design implication that I like. I no longer have to call IsValid() on the domain model. I have always put this call on the domain object because I want simple access to the validator. Putting it here makes tests much easier to write because I don’t have to instantiate a ValidatorRunner. Now with Verify and VerifyFails I can delegate the runner into this area. That would be nice and clean up the domain model. However, it does mean that I have going to have to have a implementation of the runner that is available for the UI layer too. HHmmmm, on second thoughts we’ll have to see what the code looks like!

  1. No comments yet.
  1. No trackbacks yet.