2

I am... kind of a beginner on unit testing.

I just read some Unit Testing Best Practices. Kind of understand the unit test is meant to prevent changes made on code that will break the application. And we will want create test cases on all the object's public API(getter, methods) to test the object's behavior to see if is as we are expected.

So now.. I have a little simple class I need to test:

public class Foo{
   private readonly string _text;

   public Foo(string initialText)
   {
      this._text = initialText;
   }

   public string Text {get;}

   //Some other methods that will use this Text property to parsing, comparasion etc
   public string RichTextFormat {....}
}

In here, this as in the comment, this Text property is use a lot of place for parsing, comparasion etc.

So I think is very important to ensure the Text property returns exactly what I passed inside the constructor.

Here is the test cases I wrote...

[TestMethod]
public void Text_WhenInitialTextIsNull()
{
    string initalizeText = null;
    Foo realFoo = new Foo(initalizeText);
    Assert.AreEqual(initalizeText, realFoo.Text);
}

[TestMethod]
public void Text_WhenInitialTextIsEmpty()
{
    string initalizeText = string.Empty;
    Foo realFoo = new Foo(initalizeText);
    Assert.AreEqual(initalizeText, realFoo.Text);
}

[TestMethod]
public void Text_WhenInitialTextIsOneLetter()
{
    string initalizeText = "A";
    Foo realFoo = new Foo(initalizeText);
    Assert.AreEqual(initalizeText, realFoo.Text);
}

[TestMethod]
public void Text_WhenInitialTextIsOneSpecialCharacter()
{
    string initalizeText = "!";
    Foo realFoo = new Foo(initalizeText);
    Assert.AreEqual(initalizeText, realFoo.Text);
}

[TestMethod]
public void Text_WhenInitialTextIsOneSentense()
{
    string initalizeText = "Hello, World!";
    Foo realFoo = new Foo(initalizeText);
    Assert.AreEqual(initalizeText, realFoo.Text);
}

[TestMethod]
public void Text_WhenInitialTextIsOneParagraph()
{
    string initalizeText = "Who's the Smartphone Winner? " + System.Environment.NewLine +
                           " On the smartphone front, however, iSuppli put Apple at number one," +
                           " while Strategy Analytics pointed to Samsung. " + System.Environment.NewLine +
                           " According to iSuppli, Apple shipped 35 million smartphones in the first quarter" +
                           " to Samsung's 32 million. Strategy Analytics, however, said Samsung's total was" +
                           " 44.5 million to Apple's 35.1 million. Nokia landed at number three on both lists" +
                           " with 12 percent market share. ";
    Foo realFoo = new Foo(initalizeText);
    Assert.AreEqual(initalizeText, realFoo.Text);
}

I wonder if this... is too heavy?

King Chan
  • 4,212
  • 10
  • 46
  • 78

4 Answers4

4

So I think is very important to ensure the Text property returns exactly what I passed inside the constructor.

It is a getter. It gets the _text field that you have seen is set in the constructor.

Doing anything more than checking the value returned from the getter after the class initialized is overkill, and even that may be considered testing something that does not need to be tested.

There is no point in testing that basic C# features are working as expected...

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • +1 - You don't normally need to test Getters and Setters. Any tests that use your object will end up covering the getters and setters anyway and it is behaviour/logic that you want to test, not lines of code. – Fenton Apr 27 '12 at 15:04
  • @Oded but unit test isn't mean to written to prevent change on code will break the program? So if anyone did some modificaton in the futrue on the Text property getter, which will break my program, so I should test on that? – King Chan Apr 27 '12 at 15:19
  • @KingChan- Writing tests is _expensive_. You should be writing tests that _matter_. Testing the logic of the software is more important than testing simple properties. – Oded Apr 27 '12 at 15:20
  • @Oded I see, but I have another property that have algorithm (parsing the text format) that embedded inside a property getter. In that case I should test that right? – King Chan Apr 27 '12 at 15:28
  • @KingChan - Yes, though you should probably change it from a property to a method (as one does not expect a property to be doing much work). – Oded Apr 27 '12 at 15:29
  • @Oded The property is exposed for databinding in Silverlight, so it couldn't be method. But I do agree, maybe it is better to take out the algorithm, put into method and call it inside the getter instead. And thanks. +1 – King Chan Apr 27 '12 at 15:34
  • @KingChan - I see. But if you could do the processing _before_ assigning to the backing field of the property, that would be better. – Oded Apr 27 '12 at 15:35
  • @Oded I think the person created it, is meant to do the process on demand. So it won't wastes the process time :) – King Chan Apr 27 '12 at 15:38
2

Since you are not manipulation the value passed in, I would suggest doing one test to ensure that the value is set correctly. Given that, the following test would be sufficient.

[TestMethod]
public void TextReturnsTextPassedToConstructor()
{
    string text = "A string";
    Foo foo = new Foo(text);

    Assert.AreEqual(text, foo.Text);
}

It would then only be worth adding other tests if there is additional logic or string manipulation going on in the constructor. For example if you threw an exception if the string was null or empty, or you were doing character replacement etc.

Trevor Pilley
  • 16,156
  • 5
  • 44
  • 60
0

I think you need to change the context of the tests. By that I mean don't concentrate the tests on the Text property, instead do this:

[TestMethod]
public void given_null_string_expect_null()
{
    string initalizeText = null;
    Foo realFoo = new Foo(initalizeText);
    Assert.AreEqual(initalizeText, realFoo.Text);
}

[TestMethod]
public void given_special_character_string_expect_that_character()
{
    string initalizeText = "!";
    Foo realFoo = new Foo(initalizeText);
    Assert.AreEqual(initalizeText, realFoo.Text);
}

and so on.

The point is this: What happens if you change the name of the property from Text to something else? You now have a lot of unit tests you need to rename to cater for that. My suggestion is that you name the tests based on the context of what is going on (e.g. Given a string with one letter, expect that letter). This way, if you rename things in the code, your tests are not affected.

My other point would be that you don't need to test at this level. A property is not really going to be worth your time to test. Instead your best off testing methods or integration with other classes rather then just a property.

Jason Evans
  • 28,906
  • 14
  • 90
  • 154
  • But talking about renaming.. isn't will be the same thing if someone rename the method? And what if the property contains logic? Becasue I have another property, the person put the parsing algorithm inside the Getter... – King Chan Apr 27 '12 at 15:25
  • Looking at my test examples, if you renamed the method, there is nothing in the test names to rename since they don't mention the method name. So you don't need to worry about that. As for properties - my preference if that properties should be simple i.e. almost no logic at all if possible. If you want a lot of logic then use a method instead i.e. `GetText()`. Properties should be simple if possible. – Jason Evans Apr 27 '12 at 21:05
  • Shouldn't test name should be include method/property name you are testing against? In that case is much easier to read and knows what is going on. From here: http://stackoverflow.com/questions/155436/unit-test-naming-best-practices – King Chan Apr 30 '12 at 18:06
  • The way I structure my tests are this: Say I have a class `TestClass` with a method `TestMe()`. In the project I will have a root folder named `TestClassesTests`, then a subfolder `TestMe`. In `TestMe` folder I then have the .cs files for the tests eg `given_some_context.cs`. But this is just one way of doing it, people have their own preferred methods for naming tests. – Jason Evans May 01 '12 at 10:30
0

Given the simple nature of your property, I think this is a bit overkill. For near-trivial tests on strings, I like to keep it at 3 tests: null, empty, something.

[TestMethod]
public void Test_Text_Null()
{
    Assert.AreEqual(null, new Foo(null).Text);
}

[TestMethod]
public void Test_Text_Empty()
{
    Assert.AreEqual("", new Foo("").Text);
}

[TestMethod]
public void Test_Text_Something()
{
    Assert.AreEqual("abc", new Foo("abc").Text);
}

Generally I don't try to unit test every conceivable variation of inputs for such simple things. If you encounter a bug in the future because someone modified Foo an now it breaks when you have a comma in there - add that to your unit test suite then. Unit testing along the way is good - but don't let it paralyze your ability to move on with finishing a project.

Rob S
  • 211
  • 1
  • 8