Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing Remove method without a call to Add method

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();
    }
like image 981
gt.guybrush Avatar asked Jun 23 '14 09:06

gt.guybrush


People also ask

Is it good practice to make all methods public?

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.

What is unit test cases in C#?

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.

How do you run unit testing?

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).


3 Answers

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.

like image 157
k.m Avatar answered Oct 23 '22 14:10

k.m


There are some ways to test Remove method:

  1. Mocking - mock your data structure, and call remove , so you can test calling the right methods
  2. Inheritance - make children protected. Your test class will inherit from Tag class. Now you can init children member so we can test the remove
  3. Use Add Method

I 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.

like image 21
Mzf Avatar answered Oct 23 '22 14:10

Mzf


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 :)

like image 28
alessandro Avatar answered Oct 23 '22 14:10

alessandro