Monday, 28 July 2008

Pex - test case 4

Hello,

I've got one more test scenario for Pex. From comments on my previous post
Case study 3 I understood that the way I specified the PUT was not done correctly (though Pex generated the unit test methods I expected).

A PUT should specify what the intended behaviour of the code under test should do. It should not do this like in a tradiotional unit test for a particular combination of test input but in a more general way.

Sounds reasonable but I find some difficulity in writing them. I'll try to come with a "better" PUT this time.

Our code under test is as follows (Sorry in advance for some bad formatting HTML tables later in the post.):

private string mCode;

public string Code
{
get { return mCode; }
set {
if (value == null )
{
throw new System.ArgumentNullException("Please specify a valid code. (non-null)");
}
else if (value.Length < 4)
{
throw new System.ArgumentException("Please specify a valid code. (bigger than 3 characters.");
}
else if (value.Length > 10)
{
throw new System.ArgumentException("Please specify a valid code. (max 10 characters. including XYZ prefix) ");
}
else if (value.StartsWith("XYZ") == false)
{
throw new System.ArgumentException("Please specify a valid code (starting with XYZ - uppercase !) ");
}
else
{
this.mCode = value;
}

}


With the comments for my PUT in mind , I created the following PUT:

[PexMethod()]
public void CodeTest(string aCode)
{
measurement target = new measurement();
string actual;
target.Code = aCode;
actual = target.Code;
PexValue.AddForValidation("actual", actual);
if (( aCode != null) &&
(aCode.Length > 3) &&
(aCode.Length <= 10) &&
(aCode.StartsWith("XYZ")) )
PexAssert.IsTrue(actual == aCode);

}



Running Pex on this PUT will result in several generated test cases.
(Pex reporting option : Enable via Options/Pex/Reports)






















Parameter Values
RunaCodeactualSummary/Exception
1null  ArgumentNullException:
Value cannot be null.
Parameter name: Please specify a valid code. (non-null)
(testingPEX)
2""  ArgumentException: Please specify a valid code. (bigger than 3 characters. (testingPEX)
3\0\0\0\0\0\0  ArgumentException: Please specify a valid code (starting with XYZ - uppercase !) (testingPEX)
4new string('\0', 14)  ArgumentException: Please specify a valid code. (max 10 characters. including XYZ prefix) (testingPEX)
5XYZZZZZ XYZZZZZ  


Of course we first need to specify the expected exceptions (Pex will suggest these for you !) :

  • Add attribute [PexAllowedExceptionFromAssembly(typeof(ArgumentNullException), "testingPEX")]

  • Add attribute [PexAllowedExceptionFromAssembly(typeof(ArgumentException), "testingPEX")]



The generated unit tests look like this :

public partial class measurementTest
{

[TestMethod]
[PexGeneratedBy(typeof(measurementTest))]
public void CodeTestString_20080728_141020_005()
{
PexValue.Generated.Clear();
this.CodeTest("XYZZZZZ");
PexValue.Generated.Validate("actual", "XYZZZZZ");
}

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
[PexGeneratedBy(typeof(measurementTest))]
public void CodeTestString_20080728_143541_000()
{
this.CodeTest((string)null);
}

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
[PexGeneratedBy(typeof(measurementTest))]
public void CodeTestString_20080728_143620_001()
{
this.CodeTest("");
}

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
[PexGeneratedBy(typeof(measurementTest))]
public void CodeTestString_20080728_143620_002()
{
this.CodeTest("\0\0\0\0\0\0");
}

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
[PexGeneratedBy(typeof(measurementTest))]
public void CodeTestString_20080728_143620_003()
{
this.CodeTest(new string('\0', 14));
}

}


If you would verify the code coverage results in Visual Studio or via the Pex reporting option, you would see 100% Block coverage.

Pex generated for us the strings to serve as input values. I don't know about you but I think this is pretty clever.

Still I'm a little confused about the PUT I created. If I would delete all the pexAssert statements the exercise would still be the same for the generated unit tests.


[PexMethod()]
public void CodeTest(string aCode)
{
measurement target = new measurement();
string actual;
target.Code = aCode;
actual = target.Code;
PexValue.AddForValidation("actual", actual);
}



Thanks for reading.

Best regards,


Alexander

2 comments:

Peli said...

Here's a better way to write the PUT

[PexMethod]
public void TestCode(string code)
{
var target = new Measurement();
target.Code = code;

// target.Code invariant
Assert.IsTrue(target.Code != null);
Assert.IsTrue(target.Code.Length >= 4);
Assert.IsTrue(target.Code.Length <= 10);
Assert.IsTrue(target.Code.StartsWith("XYZ"));

// at this point, the following should hold
Assert.IsTrue(code == target.Code);
}

(note that the first 4 assertions could be refactored as an invariant for the measurement class)

Peli said...

Another tip: Ctrl + A, Ctrl + C will copy the values of the parameter table into the clipboard in a CSV format. From there, you can easily paste it in your editor :)