Jan Jan
Jan Jan

Reputation: 11

How would I be able to unit test a void method?

I want to test my method which checks for the direction of movement of vehicles. It is a void method and has one parameter - integer.

As far as I know all the if's should be split into different test methods but I am not aware of how to do that.

/// <summary>
        /// checks the east neighbour of the crossing on a given cell
        /// </summary>
        /// <param name="cellNr">the cell Nr of the crossing whose neighbour ///its checked</param>

    public void CheckEast(int cellNr)
    {
        Crossing cr = Cells[cellNr].Crossing;

        //If there is east neighbour - laneIns of the current crossing with direction east are NOT endLane
        if (cells[cellNr + 1].Taken)
        {
            foreach (LaneIn laneIn in cr.LaneInList)
            {
                if (laneIn.Direction == "west")
                {
                    laneIn.EndLane = false;
                }
            }

            //car should NOT be spawned from east && LaneOut of current crossing with direction east is NOT endLane
            cr.IncomingStreams[3] = "";
            cr.LaneOutList[0].EndLane = false;

            //LaneIn 'east' of the east neighbour is NOT endLane
            foreach (LaneIn laneIn in cells[cellNr + 1].Crossing.LaneInList)
            {
                if (laneIn.Direction == "east")
                {
                    laneIn.EndLane = false;
                }
            }
            //no spawned car in the neighbour from the east laneIns and LaneOut 'west' is not endlane
            cells[cellNr + 1].Crossing.IncomingStreams[1] = "";
            cells[cellNr + 1].Crossing.LaneOutList[1].EndLane = false;
            cr.Neighbours[1] = cells[cellNr + 1].Crossing;
            cells[cellNr + 1].Crossing.Neighbours[3] = cr;
        }
        //if there is NO neighbour on east side, laneIns of the current crossing are endlane(spawning point)
        //laneout is endlane, cars are erased after leaving it
        else
        {
            cr.Neighbours[1] = null;
            cr.IncomingStreams[3] = "west";
            cr.LaneOutList[0].EndLane = true;
            foreach (LaneIn laneIn in cr.LaneInList)
            {
                if (laneIn.Direction == "west")
                {
                    laneIn.EndLane = true;
                }
            }
        }
    }

Upvotes: 0

Views: 52

Answers (2)

Scott Hannen
Scott Hannen

Reputation: 29312

To literally answer your question, if a method has no return value then it must produce some other side effect. (If it doesn't return anything or have a side effect then it doesn't do anything.)

If method changes the state of the class itself you can execute the method and assert for the expected state:

public class Counter
{
    public int Value { get; private set; }

    public void Increment() => Value++;
}
public void Counter_increments()
{
    var subject = new Counter();
    subject.Increment();
    Assert.AreEqual(1, subject.Value());
}

Or perhaps the behavior that you want to test is your class's interaction with some dependency. There are a few ways to do that. One is to check the state of the dependency:

public class ClassThatIncrementsCounter
{
    public readonly Counter _counter;

    public ClassThatIncrementsCounter(Counter counter)
    {
        _counter = counter;
    }

    public void DoSomething()
    {
        // does something and then increments the counter
        _counter.Increment();
    }
}

[TestMethod]
public void DoSomething_increments_counter()
{
    var counter = new Counter();
    var subject = new ClassThatIncrementsCounter(counter);
    subject.DoSomething();
    Assert.AreEqual(1, counter.Value);
}

You can also use a library like Moq to verify that your class interacted with a dependency:

public class ClassThatIncrementsCounter
{
    public readonly ICounter _counter;

    public ClassThatIncrementsCounter(ICounter counter)
    {
        _counter = counter;
    }

    public void DoSomething()
    {
        // does something and then increments the counter
        _counter.Increment();
    }
}

[TestMethod]
public void DoSomething_increments_counter()
{
    var counter = new Mock<ICounter>();

    // indicate that we want to track whether this method was called.
    counter.Setup(x => x.Increment()).Verifiable();
    var subject = new ClassThatIncrementsCounter(counter.Object);
    subject.DoSomething();

    // verify that the method was called.
    counter.Verify(x => x.Increment());
}

In order for this to work well, as you've noted, it's necessary to break methods down into smaller chunks that we can test in isolation. If a method makes lots of decisions then in order to test fully we'd need to test for every applicable combination, or every possible branch along which the code can execute. That's why a method with lots of conditions can be harder to test.

I spent some time looking at your code, but it's not clear enough what it's actually doing to make it easy to suggest how to refactor it.

You can take larger chunks of code which get repeated like these:

        foreach (LaneIn laneIn in cr.LaneInList)
        {
            if (laneIn.Direction == "west")
            {
                laneIn.EndLane = false;
            }
        }

        foreach (LaneIn laneIn in cr.LaneInList)
        {
            if (laneIn.Direction == "west")
            {
                laneIn.EndLane = true;
            }
        }

...and replace them with methods like this, with the exception that you would give them clear, meaningful names. I can't do that since I'm not sure what they do:

public void SetEndLaneInDirection(List<LaneIn> laneInList, string direction, bool isEnd)
{
    foreach (LaneIn laneIn in laneInList)
    {
        if (laneIn.Direction == direction)
        {
            laneIn.EndLane = isEnd;
        }
    }
}

Now that one smaller piece of code is easier to test. Or, if you have a block of methods that all make some related update like this:

        cells[cellNr + 1].Crossing.IncomingStreams[1] = "";
        cells[cellNr + 1].Crossing.LaneOutList[1].EndLane = false;
        cr.Neighbours[1] = cells[cellNr + 1].Crossing;
        cells[cellNr + 1].Crossing.Neighbours[3] = cr;

Then put those in a method, and again, give it a clear name.

Now you can set up the class, call the method, and assert what you expect the state to be.

The side effect is that as you replace chunks of code with well-named method calls, your code becomes easier to read, because the method calls say, in near-English, what each step is doing.

It's much harder to do after the fact. Part of the challenge is learning to write code that's already easier to test. The good news is that as we do so our code naturally becomes simpler and easier to follow. And we have tests.

Here's a great article on how to refactoring existing code to make it easier to test.

Upvotes: 2

RobertBaron
RobertBaron

Reputation: 2854

In your unit test, you call CheckEast with cellNr set to some values, and then you assert that the expected side effects (that is, changes) were made to the Cells, LaneInList, etc., data structures.

Upvotes: 1

Related Questions