繁体   English   中英

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

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

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

据我所知,所有的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;
                }
            }
        }
    }

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

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

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;

然后将它们放在方法中,并再次给它起一个清晰的名称。

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

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

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

这是一篇有关如何重构现有代码以使其更易于测试的出色文章

在你的单元测试,你叫CheckEastcellNr设置为某个值,然后断言预期的副作用(即,改变)是在制造CellsLaneInList等,数据结构。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM