我将如何对void方法进行单元测试?

时间:2019-05-30 00:16:32

标签: c# unit-testing

我想测试一下检查车辆运动方向的方法。这是一个空方法,具有一个参数-整数。

据我所知,所有if均应分为不同的测试方法,但我不知道该怎么做。

/// <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;
                }
            }
        }
    }

2 个答案:

答案 0 :(得分:2)

要从字面上回答您的问题,如果方法没有返回值,则它必须产生其他副作用。 (如果它不返回任何东西或没有副作用,那么它什么也不做。)

如果方法更改了类本身的状态,则可以执行该方法并声明所需的状态:

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());
}

或者您要测试的行为可能是类与某些依赖项之间的交互。有几种方法可以做到这一点。一种是检查依赖项的状态:

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);
}

您还可以使用Moq之类的库来验证您的类是否与依赖项进行了交互:

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());
}

为使此方法正常工作,如您所指出的,有必要将方法分解为较小的块,以便我们可以单独进行测试。如果一个方法做出了很多决定,那么为了进行全面测试,我们需要针对每种适用的组合或代码可以执行的每个可能的分支进行测试。这就是为什么具有很多条件的方法更难测试的原因。

我花了一些时间查看您的代码,但是还不清楚它实际上在做什么,以至于很难建议如何重构它。

您可以采用较大的代码块,使其重复执行如下操作:

        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;
            }
        }

...并用类似的方法替换它们,除了为它们赋予清晰,有意义的名称。我不能这样做,因为我不确定他们做什么:

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

现在,一小段代码更易于测试。或者,如果您有全部都进行如下相关更新的方法块:

        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;

然后将其放入方法中,并再次为其指定一个清晰的名称。

现在,您可以设置类,调用方法并声明您期望的状态。

副作用是,当您用命名良好的方法调用替换代码块时,您的代码将变得更易于阅读,因为该方法调用以近似英语的形式说明了每个步骤的作用。

事后很难做。挑战的一部分是学习编写易于测试的代码。好消息是,随着我们这样做,我们的代码自然会变得更加简单和易于遵循。我们有测试。

这里有great article,介绍如何重构现有代码以使其更易于测试。

答案 1 :(得分:1)

在单元测试中,您将CheckEast设置为某些值的情况下调用cellNr,然后断言对Cells产生了预期的副作用(即更改) ,LaneInList等数据结构。