Sunday, 27 May 2012

Code Contracts

I started writing a blog post about Code Contracts that I believe will end up working its way into all future .NET software, particularly APIs.  Then I came across this excellent write up that I highly recommend:

http://devjourney.com/blog/code-contracts-part-1-introduction/

Code Contracts in action

using System;
using System.Diagnostics.Contracts;

namespace CodeContracts
{
public interface IRandomGenerator
{
int Next(int min, int max);
}

public class RandomGenerator : IRandomGenerator
{
private readonly Random _random = new Random();

public int Next(int min, int max)
{
return _random.Next(min, max);
}
}

public class Randomizer
{
private readonly IRandomGenerator _generator;

public Randomizer(IRandomGenerator generator)
{
_generator = generator;
}

public int GetRandomFromRangeContracted(int min, int max)
{
Contract.Requires<ArgumentOutOfRangeException>(
min < max,
"Min must be less than max"
);

Contract.Ensures(
Contract.Result<int>() >= min &&
Contract.Result<int>() <= max,
"Return value is out of range"
);

return _generator.Next(min, max);
}
}
}

The above code Post build

public class Randomizer
{
// Fields
private readonly IRandomGenerator _generator;

// Methods
public Randomizer(IRandomGenerator generator)
{
this._generator = generator;
}

public int GetRandomFromRangeContracted(int min, int max)
{
int Contract.Old(min);
int Contract.Old(max);
__ContractsRuntime.Requires<ArgumentOutOfRangeException>(min < max, "Min must be less than max", "min < max");
try
{
Contract.Old(min) = min;
}
catch (Exception exception1)
{
if (exception1 == null)
{
throw;
}
}
try
{
Contract.Old(max) = max;
}
catch (Exception exception2)
{
if (exception2 == null)
{
throw;
}
}
int CS$1$0000 = this._generator.Next(min, max);
int Contract.Result() = CS$1$0000;
__ContractsRuntime.Ensures((Contract.Result() >= Contract.Old(min)) && (Contract.Result() <= Contract.Old(max)), "Return value is out of range", "Contract.Result<int>() >= min && Contract.Result<int>() <= max");
return Contract.Result();
}
}


Unit tests with Moq and NUnit test cases

using System;
using Moq;
using NUnit.Framework;

namespace CodeContracts.Tests
{
[TestFixture]
public class Given_mocked_RandomGenerator
{
private Mock<IRandomGenerator> _randomMock;
private Randomizer _randomizer;

[SetUp]
public void Setup()
{
_randomMock = new Mock<IRandomGenerator>();
_randomizer = new Randomizer(_randomMock.Object);
}

[TestCase(1, 0)]
[TestCase(2, 1)]
[TestCase(100, 10)]
[TestCase(0, -1)]
[TestCase(-1, -2)]
[TestCase(-10, -100)]
public void When_min_is_greater_than_max_Then_should_throw_exception(int min, int max)
{
Assert.Catch<Exception>(() => _randomizer.GetRandomFromRangeContracted(min, max));
}

[TestCase(0, 0)]
[TestCase(1, 1)]
[TestCase(10000, 10000)]
[TestCase(-1, -1)]
[TestCase(-10000, -10000)]
public void When_min_is_equal_to_max_Then_should_throw_exception(int min, int max)
{
Assert.Catch<Exception>(() => _randomizer.GetRandomFromRangeContracted(min, max));
}

[TestCase(1, 2, 0)]
[TestCase(10, 100, 7)]
[TestCase(-100, -10, -107)]
public void When_return_value_is_less_than_min_Then_should_throw_exception(int min, int max, int expected)
{
_randomMock
.Setup(r => r.Next(min, max))
.Returns(expected);

Assert.Catch<Exception>(() => _randomizer.GetRandomFromRangeContracted(min, max));
}

[TestCase(10, 100, 102)]
public void When_return_value_is_more_than_max_Then_should_throw_exception(int min, int max, int expected)
{
_randomMock
.Setup(r => r.Next(min, max))
.Returns(expected);

Assert.Catch<Exception>(() => _randomizer.GetRandomFromRangeContracted(min, max));
}

[TestCase(0, 2, 1)]
[TestCase(10, 100, 50)]
[TestCase(-100, 10, 5)]
[TestCase(int.MinValue, int.MaxValue, 1)]
[TestCase(int.MinValue, int.MaxValue, 0)]
[TestCase(int.MinValue, int.MaxValue, -1)]
public void When_min_is_less_than_max_Then_should_equal_expected_result(int min, int max, int expected)
{
_randomMock
.Setup(r => r.Next(min, max))
.Returns(expected);

var actual = _randomizer.GetRandomFromRangeContracted(min, max);

Assert.AreEqual(expected, actual);
}

[TestCase(0, 2, 0)]
[TestCase(10, 100, 10)]
[TestCase(-100, 10, -100)]
[TestCase(int.MinValue, int.MaxValue, int.MinValue)]
public void When_min_is_less_than_max_and_result_equals_min_Then_should_equal_expected_result(int min, int max, int expected)
{
_randomMock
.Setup(r => r.Next(min, max))
.Returns(expected);

var actualResult = _randomizer.GetRandomFromRangeContracted(min, max);

Assert.AreEqual(expected, actualResult);
}

[TestCase(0, 2, 2)]
[TestCase(10, 100, 100)]
[TestCase(-100, 10, 10)]
[TestCase(int.MinValue, int.MaxValue, int.MaxValue)]
public void When_min_is_less_than_max_and_result_equals_max_Then_should_equal_expected_result(int min, int max, int expected)
{
_randomMock
.Setup(r => r.Next(min, max))
.Returns(expected);

var actualResult = _randomizer.GetRandomFromRangeContracted(min, max);

Assert.AreEqual(expected, actualResult);
}
}
}

Source


http://stevenhollidge.com/blog-source-code/CodeContracts.zip

No comments:

Post a Comment