Contracts replace parameter and property validation. Let's consider a class that represents a box. It has length, height, and width properties that must be greater than zero - otherwise the box doesn't exist!
The PostSharp Contracts documentation is good, but I couldn't find a list of all the contracts anywhere so here's the ones I could find.
- CreditCard
- EmailAddress
- EnumDataType
- GreaterThan
- LessThan
- Negative
- NotEmpty
- NotNull
- Phone
- Positive
- Range
- RegularExpression
- Required
- StrictlyGreaterThan
- StrictlyLessThan
- StrictlyNegative
- StrictlyPositive
- StrictRange
- StringLength
And, of course, you can create custom contracts.
Start a new Visual Studio project called Box. I'm using 2019, C#, and .Net Framework. Rename Class1 to Box. The initial code for Box is...
namespace Box
{
public class Box
{
public int length {
get; set; }
public int width { get; set; }
public int height {
get; set; }
public Box() { }
public static Box
CreateBoxFromLengthWidthHeight(int length, int width, int height)
{
return new Box() {
length = length, height = height, width = width };
}
}
}
We would normally write validation logic into the property setters but this gets messy.
private int _length;
public int length
{
get { return _length;
}
set
{
if (value <= 0)
throw new System.Exception("length must be
greater than zero");
else
_length = value;
}
}
using PostSharp.Patterns.Contracts;
namespace Box
{
public class Box
{
[StrictlyGreaterThan(0)]
public int length {
get; set; }
[StrictlyGreaterThan(0)]
public int width { get; set; }
[StrictlyGreaterThan(0)]
public int height {
get; set; }
public Box() { }
public static Box
CreateBoxFromLengthWidthHeight(int length, int width, int height)
{
return new Box() {
length = length, height = height, width = width };
}
}
}
Now add a test class to the Box.cs file. I put it inside the Box namespace.
using PostSharp.Patterns.Contracts;
using Xunit;
namespace Box
{
public class Box
{
[StrictlyGreaterThan(0)]
public int length {
get; set; }
[StrictlyGreaterThan(0)]
public int width { get; set; }
[StrictlyGreaterThan(0)]
public int height {
get; set; }
public Box() { }
public static Box
CreateBoxFromLengthWidthHeight(int length, int width, int height)
{
return new Box() {
length = length, height = height, width = width };
}
}
public class TestBox
{
[Theory]
[InlineData(1,1,1)]
[InlineData(100,100,100)]
[InlineData(0,1,1)]
[InlineData(1,0,1)]
[InlineData(1,1,0)]
public void
TestBoxConstructor(int length, int width, int height)
{
Box box =
Box.CreateBoxFromLengthWidthHeight(length, width, height);
}
}
}
Of course we really want all our tests to pass so let's rewrite the TestBox class and split it into a set of tests that should pass and a set of tests that should throw an exception...
public class TestBox
{
[Theory]
[InlineData(1,1,1)]
[InlineData(100,100,100)]
public void
TestBoxConstructorPass(int length, int width, int height)
{
Box box =
Box.CreateBoxFromLengthWidthHeight(length, width, height);
}
[Theory]
[InlineData(0, 1, 1)]
[InlineData(1, 0, 1)]
[InlineData(1, 1, 0)]
public void
TestBoxConstructorException(int length, int width, int height)
{
Box box;
Assert.Throws<ArgumentOutOfRangeException>(() => box =
Box.CreateBoxFromLengthWidthHeight(length, width, height));
}
}
No comments:
Post a Comment