例如,我有以下方法:
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
{
}
扔掉它们并抓住它们。更准确地说,但代码更多。虚拟代码。
哪种方式更正确?
答案 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
,但由您来决定。如果你有富有表现力的测试名称并且代码足够自我记录,你就会知道无论如何都会引用什么参数。