正确处理异常

时间:2014-10-19 09:30:46

标签: c# unit-testing exception

例如,我有以下方法:

        public void MeetingNoteSave(int MeetingID, string note, bool IsInviter, string Username)
        {
                meeting = Get<Meeting>(p => p.MeetingID == MeetingID && p.UserInviter.aspnet_User.UserName == Username);
                MeetingNoteSaveCheckings(meeting, MeetingID);
// some actions here
        }

        void MeetingNoteSaveCheckings(Meeting meeting, int MeetingID)
        {
            DateTime currentDateWithTime = DateTime.Now;
            if (meeting == null)
            {
                throw new Exception("Meeting does not exist. MeetingID=" + MeetingID);
            }
            DateTime meetingTime = meeting.MeetingTime.Day.AddHours(meeting.MeetingTime.Hour).AddMinutes(meeting.MeetingTime.Minute);
            if (meetingTime > currentDateWithTime)
            {
                throw new Exception("Meeting is future. MeetingID=" + MeetingID + ". Meeting time = '" + meetingTime + "', Current time='" + currentDateWithTime + "'");
            }
        }

所以,方法可以抛出2个异常 - 当根本不存在这些参数或满足当前时间(应该是过去或当前时间)时。

现在,我正在编写单元测试。简单的方法:

    [TestMethod]
    public void MeetingNoteSave()
    {
        _repository.MeetingNoteSave(1, "My note", true, "xxx@xxx.com");
    }

当然,呼叫单元测试将失败并带有一些参数。我想抓住这些案例,所以,测试应该是成功的。我可以用两种方式做。首先很简单,但有点脏:

        try
        {
            _repository.MeetingNoteSave(1, "My note", true, "xxx@xxx.com");
        }
        catch(Exception ex)
        {
            if (ex.Message.IndexOf("Meeting does not exist")>=0)
            {
                // some actions
            }

            if (ex.Message.IndexOf("Meeting is future")>=0)
            {
                // some actions
            }
        }

因此,测试将成功使用不正确的输入参数(因此,单元测试可用于测试具有不正确参数的方法),但失败并遇到错误。好。

其他方式 - 创建特殊的虚拟异常,如MeetingNullException和MeetingFutureException

public class MeetingNullException : Exception
{
}

public class MeetingFutureException : Exception
{
}
扔掉它们并抓住它们。更准确地说,但代码更多。虚拟代码。 哪种方式更正确?

1 个答案:

答案 0 :(得分:3)

两者都没有,它们都存在缺陷。你的第二种方法是正确的方向:你应该避免抛出Exception类型的一般例外;特定的子类更具表现力。

您在测试中需要做的是使用[ExpectedException]属性,这将使它们看起来像这样:

[TestMethod]
[ExpectedException(typeof(MeetingNullException))]
public void MeetingNoteSave_WithNotExistingMeeting()
{
    _repository.MeetingNoteSave(1, "My note", true, "xxx@xxx.com");
}

[TestMethod]
[ExpectedException(typeof(MeetingFutureException ))]
public void MeetingNoteSave_WithFutureDate()
{
    _repository.MeetingNoteSave(1, "My note", true, "xxx@xxx.com");
}

确保每个可能的程序流只有一个测试:2个例外意味着2个测试。就个人而言,我可能会避免创建特定的子类,只使用ArgumentException,但由您来决定。如果你有富有表现力的测试名称并且代码足够自我记录,你就会知道无论如何都会引用什么参数。