I am writing test for a class thats manage trees of Tag objects:
public class Tag
{
public virtual int Id { get; set; }
public virtual string Description{ get; set; }
private IList<Tag> children = new List<Tag>();
public virtual IEnumerable<Tag> Children
{
get {return children .ToArray();}
}
public void AddChildTag(Tag child)
{
children.Add(child);
}
public void RemoveChildTag(Tag child)
{
children.Remove(child);
}
}
As you can see the only mode to set the parent property is via the AddChildTag
method and this is exactly what i want, my problem is in unit test: since every test should be atomic, how can i test the RemoveChildTag
method?
Only way i see is a call to the add method and later to the remove, but in this way if Add as some errors, even the test of remove will fail, so atomicity is lost.
How can that be done?
EDIT
I removed parent property from Tag object, since i no more use it
Some test according to solution using NUnit and FluentAssertion
[Test]
public void AddChildTagAddsChildren()
{
//Arrange
Tag parent = new Tag();
Tag child = new Tag();
//Act
parent.AddChildTag(child);
//Assert
parent.Children.Should().Contain(child);
}
[Test]
public void RemoveChildTagRemovesAddedChildren()
{
//Arrange
Tag parent = new Tag();
Tag child = new Tag();
parent.AddChildTag(child);
//Act
parent.RemoveChildTag(child);
//Assert
parent.Children.Should().NotContain(child);
}
[Test]
public void RemoveChildTagThrowsNothingWhenNoChild()
{
//Arrange
Tag parent= new Tag();
Tag child= new Tag();
//Act
Action RemoveChild = () => parent.RemoveChildTag(child);
//Assert
RemoveChild.ShouldNotThrow();
}
Yes it is very bad practice - you're letting your tools make design decisions for you. I think the main problem here is that you're trying to treat each individual method as a unit. This is generally the cause of all unit test woes.
A Unit Test is a code written by any programmer which test small pieces of functionality of big programs. Performing unit tests is always designed to be simple, A "UNIT" in this sense is the smallest component of the large code part that makes sense to test, mainly a method out of many methods of some class.
To run all the tests in a default group, choose the Run icon and then choose the group on the menu. Select the individual tests that you want to run, open the right-click menu for a selected test and then choose Run Selected Tests (or press Ctrl + R, T).
Your unit tests should reflect actual use cases of your class. How will your consumers use RemoveChildTag
method? Which makes more sense? Which is how you'd use collection of objects?
var parent = new Tag();
// later
parent.RemoveChildTag(child);
… or
var parent = new Tag();
parent.AddChildTag(child);
// later
parent.RemoveChildTag(child);
Your consumers will remove objects they previously added. This is your use case, "Remove removes elements previously added" (note that it also produces excellent test method name).
Add
and Remove
methods are often complementary - you can't test one without the other.
There are some ways to test Remove
method:
children
protected. Your test class will inherit from Tag
class. Now you can init children
member so we can test the removeAdd
MethodI think option 3 is the best, it's ok using other methods in your unit testing, if Add
have some error , more then 1 test will fail - you should be able to understand what you need to fix.
Also, each unit test should test some basic behavior, even if you need to do some preparation before. If those preparation failed, fail the test with the relevant comments
Option 1 - is best when you code is reaching to 3rd party - other server, file system & more. Since you don't want to make those calls in unit test - mock the response.
Option 2 - best I can say , is when you want to test protected / private method, without needing to make all the calls "on the way" that your code does (public method that make many calls that eventually call the method you want to test), since you want to test only a specific logic. It also easy to use this option when your class have some state that you want to test, without needing to write a lot of code to bring your class to this state.
You could use an PrivateObject Class to Arrange your object under test
Allows test code to call methods and properties on the code under test that would be inaccessible because they are not public.
EDIT
then you could get full access to wrapped object by PrivateObject.RealType and PrivateObject.Target Properties
EDIT
Anyway, every system which break the segregation and encapsulation of a class make useless the black box approach of Unit Testing in TDD and should be avoided as a poison :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With