Oct 14, 2008

Tricky Path.Combine Thing

About 10 minutes I needed to understand the problem. Luckily I had tests. It appears, that Path.Combine works funny. Here are a comple of examples:

  1. If you do Path.Combine("../Tests/", "/123.xml") the result will be "/123.xml"
  2. If you do Path.Combine("../Tests/", "123.xml") the result will be "../Tests/123.xml"
  3. Finally, if you do Path.Combine("../Tests", "123.xml") the result will be "../Tests\123.xml"

Oh, frankly speaking, I would really like to have Path.Combine("../Tests/", "/123.xml")  give me "../Tests/123.xml" or, at least, "../Tests//123.xml" and even "../Tests\123.xml" would be fine.

For now, I have to remember funny point #1 and will try to do #2 and #3 all along.

Oct 7, 2008

XML in .Net: Frequently Asked Questions

XML Team started to blog about most difficult things in System.XML. The list of posts below:

Hope this helps!

Technorati Tags:

Test Requirements First

Today I found a post about The Compare Contract by BCL Team. In brief, one of the customers complained about the breaking changes made in SP1 compared to RTM version. Actually, the ComparedTo method of the StringWrapper class was changed which affected sorting behavior.

It appeared that ComparedTo method was implemented incorrectly. Therefore, it was decided to change it. MSDN describes several requirements that must be met by a particular implementation of IComparable<T> interface.

I see this as a big bonus for any developer implementing IComparable<T> interface. Even before starting to implement the method you can write very strict Unit Tests that will help you to implement a method that will satisfy common requirements for any sorting algorithm.

As you see, you can end up with 5 unit test. If you will use MbUnit, for example, you can employ Row Tests to specify more precise requirements for your interface implementation very easy.

For example I ended up with 15 tests. It took me about 15 minutes. Here they are:

namespace CompareToContract.UnitTests
{
using MbUnit.Framework;

[TestFixture]
public class StringWrapperTests
{
[RowTest]
[Row("")]
[Row("value")]
[Row(null)]
public void CompareTo_SameValue_ShouldReturnZero(string value)
{
StringWrapper wrapper = new StringWrapper() { Value = value };
Assert.AreEqual(wrapper.CompareTo(wrapper), 0, "A.CompareTo(A) should return 0.");
}

[RowTest]
[Row("", "")]
[Row("value", "value")]
[Row(null, null)]
public void CompareTo_AnotherEqualValue_ShouldReturnZero(string value1, string value2)
{
StringWrapper a = new StringWrapper() { Value = value1 };
StringWrapper b = new StringWrapper() { Value = value2 };

Assert.AreEqual(a.CompareTo(b), 0, "A'value'.ComparedTo(b'value') should return 0.");
}

[RowTest]
[Row("", "", "")]
[Row("value", "value", "value")]
[Row(null, null, null)]
public void CompareTo_AandBandCequal_ShouldReturnZero(string value1, string value2, string value3)
{
StringWrapper a = new StringWrapper() { Value = value1 };
StringWrapper b = new StringWrapper() { Value = value2 };
StringWrapper c = new StringWrapper() { Value = value3 };

Assert.AreEqual(a.CompareTo(b), 0, "A'value'.ComparedTo(B'value') should return 0.");
Assert.AreEqual(b.CompareTo(c), 0, "B'value'.ComparedTo(C'value') should return 0.");
Assert.AreEqual(a.CompareTo(c), 0, "A'value'.ComparedTo(C'value') should return 0.");
}

[RowTest]
[Row("1", "2")]
[Row("value1", "value2")]
[Row(null, "1")]
public void CompareTo_IfAComparedToBReturnValueThenBComparedToA_ShouldReturnSameValueButDifferentInSign(string value1, string value2)
{
StringWrapper a = new StringWrapper() { Value = value1 };
StringWrapper b = new StringWrapper() { Value = value2 };

Assert.AreEqual(a.CompareTo(b), -1 * b.CompareTo(a), "a.ComparedTo(b) should equal to -1 * b.ComparedTo(a).");
}

[RowTest]
[Row("1", "2", "3")]
[Row("value3", "value2", "value1")]
[Row("value1", "value2", "value3")]
public void CompareTo_IfAComparedToBReturnsValueWithTheSameSignAsBComparedToCThenACompareToC_ShouldReturnValueWithTheSameSign(string value1, string value2, string value3)
{
StringWrapper a = new StringWrapper() { Value = value1 };
StringWrapper b = new StringWrapper() { Value = value2 };
StringWrapper c = new StringWrapper() { Value = value3 };

int abResult = a.CompareTo(b);
int bcResult = b.CompareTo(c);
int acResult = a.CompareTo(c);

Assert.IsTrue(Math.Sign(abResult) == Math.Sign(bcResult), "A.ComparedTo(B) should have the same sign as B.ComparedTo(C).");
Assert.IsTrue(Math.Sign(abResult) == Math.Sign(acResult), "A.ComparedTo(C) should have the same sign as A.ComparedTo(B).");
}
}
}



The old implementation of CompareTo completed with 10 succeeded and 5 failed tests. While implementation provided by BCL Team completed with all 15 succeeded. This way I can trust this implementation of CompareTo method.



Conclusion



Test your requirements before you right the code. This will minimize your time and efforts to understand (remember all the time) requirements of implementation and will help you to write the code you need, not the code you can write.



Hope this helps!



Technorati Tags: ,,