C'est une question tout à fait pertinente.
Tout d'abord, beaucoup de gens sont ce qui suggère que vous êtes à l'aide d'assertions à tort. Je pense que beaucoup de débogage experts sont en désaccord. Bien qu'il soit recommandé de vérifier les invariants avec des assertions les assertions ne devrait pas être limitée à l'état d'invariants. En fait, de nombreux experts débogueurs vous dira de faire valoir que les conditions qui peuvent provoquer une exception en plus de vérifier les invariants.
Par exemple, considérons le code suivant:
if (param1 == null)
throw new ArgumentNullException("param1");
C'est très bien. Mais lorsque l'exception est levée, la pile devient déroulée jusqu'à ce que quelque chose gère l'exception (probablement un haut niveau de gestionnaire par défaut). Si l'exécution s'arrête à ce point (vous pouvez avoir une modale de dialogue d'exception dans une application Windows), vous avez la possibilité d'attacher un débogueur, mais vous avez probablement perdu beaucoup d'informations qui pourraient vous ont aidé à résoudre le problème, car la plupart de la pile a été dénouée.
Maintenant, considérez les points suivants:
if (param1 == null)
{
Debug.Fail("param1 == null");
throw new ArgumentNullException("param1");
}
Maintenant, si le problème se produit, le modal affirmer boîte de dialogue apparaît. L'exécution est suspendue instantanément. Vous êtes libre de fixer votre choix débogueur et enquêter sur exactement ce qui est sur la pile et l'état du système au point exact de la panne. Dans un communiqué de construire, vous obtenez toujours une exception.
Maintenant, comment faire nous nous occupons de vos tests unitaires?
Considérons une unité de test qui permet de tester le code ci-dessus qui comprend l'affirmation. Vous voulez vous assurer que l'exception est levée lorsque param1 est null. Vous s'attendre à ce que de revendication particulière à l'échec, mais n'importe quelle autre affirmation échecs indique que quelque chose est faux. Vous souhaitez permettre de revendication particulière des échecs pour les essais particuliers.
Pour résoudre cela dépendra de ce que les langues etc. vous êtes en utilisant. Cependant, j'ai quelques suggestions si vous êtes en utilisant .NET (je n'ai pas vraiment essayé, mais je le ferai dans l'avenir et de mettre à jour le post):
- Vérifier La Trace.Les auditeurs. Trouver toutes les instances de DefaultTraceListener et définir AssertUiEnabled à false. Cela empêche la boîte de dialogue modale à partir de popping up. Vous pouvez également effacer les auditeurs de la collection, mais vous n'aurez pas de traçage que ce soit.
- Écrivez votre propre TraceListener qui enregistre des assertions. Comment vous enregistrez des assertions est à vous. L'enregistrement du message d'erreur peut ne pas être assez bon, de sorte que vous pouvez parcourir la pile de trouver la méthode de l'affirmation venue et d'enregistrement que trop.
- Une fois qu'un test se termine, vérifiez que la seule affirmation des échecs qui ont eu lieu ont été ceux que vous attendiez. Si toutes les autres sont survenus, ne passent pas le test.
Pour un exemple de TraceListener qui contient le code pour faire une pile de marche comme ça, j'aurais de la recherche pour SUPERASSERT.NET's SuperAssertListener et vérifier son code. (Il est également intéressant de l'intégration SUPERASSERT.NET si vous êtes vraiment sérieux au sujet de débogage à l'aide d'assertions).
La plupart des infrastructures de test unitaire de soutien de la configuration de test/méthodes de démontage. Vous pouvez ajouter du code pour réinitialiser l'écouteur de suivi et d'affirmer qu'il n'y en a pas à tous les imprévus affirmation des échecs dans ces domaines afin de réduire la duplication et éviter les erreurs.
Mise à JOUR:
Voici un exemple TraceListener qui peut être utilisé pour l'unité de test affirmations. Vous devez ajouter une instance de la Trace.Les auditeurs de la collection. Vous voudrez aussi de fournir facilement de manière à ce que vos tests peuvent obtenir de l'auditeur.
NOTE: Ceci doit beaucoup à John Robbins SUPERASSERT.NET.
/// <summary>
/// TraceListener used for trapping assertion failures during unit tests.
/// </summary>
public class DebugAssertUnitTestTraceListener : DefaultTraceListener
{
/// <summary>
/// Defines an assertion by the method it failed in and the messages it
/// provided.
/// </summary>
public class Assertion
{
/// <summary>
/// Gets the message provided by the assertion.
/// </summary>
public String Message { get; private set; }
/// <summary>
/// Gets the detailed message provided by the assertion.
/// </summary>
public String DetailedMessage { get; private set; }
/// <summary>
/// Gets the name of the method the assertion failed in.
/// </summary>
public String MethodName { get; private set; }
/// <summary>
/// Creates a new Assertion definition.
/// </summary>
/// <param name="message"></param>
/// <param name="detailedMessage"></param>
/// <param name="methodName"></param>
public Assertion(String message, String detailedMessage, String methodName)
{
if (methodName == null)
{
throw new ArgumentNullException("methodName");
}
Message = message;
DetailedMessage = detailedMessage;
MethodName = methodName;
}
/// <summary>
/// Gets a string representation of this instance.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Format("Message: {0}{1}Detail: {2}{1}Method: {3}{1}",
Message ?? "<No Message>",
Environment.NewLine,
DetailedMessage ?? "<No Detail>",
MethodName);
}
/// <summary>
/// Tests this object and another object for equality.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
var other = obj as Assertion;
if (other == null)
{
return false;
}
return
this.Message == other.Message &&
this.DetailedMessage == other.DetailedMessage &&
this.MethodName == other.MethodName;
}
/// <summary>
/// Gets a hash code for this instance.
/// Calculated as recommended at http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return
MethodName.GetHashCode() ^
(DetailedMessage == null ? 0 : DetailedMessage.GetHashCode()) ^
(Message == null ? 0 : Message.GetHashCode());
}
}
/// <summary>
/// Records the assertions that failed.
/// </summary>
private readonly List<Assertion> assertionFailures;
/// <summary>
/// Gets the assertions that failed since the last call to Clear().
/// </summary>
public ReadOnlyCollection<Assertion> AssertionFailures { get { return new ReadOnlyCollection<Assertion>(assertionFailures); } }
/// <summary>
/// Gets the assertions that are allowed to fail.
/// </summary>
public List<Assertion> AllowedFailures { get; private set; }
/// <summary>
/// Creates a new instance of this trace listener with the default name
/// DebugAssertUnitTestTraceListener.
/// </summary>
public DebugAssertUnitTestTraceListener() : this("DebugAssertUnitTestListener") { }
/// <summary>
/// Creates a new instance of this trace listener with the specified name.
/// </summary>
/// <param name="name"></param>
public DebugAssertUnitTestTraceListener(String name) : base()
{
AssertUiEnabled = false;
Name = name;
AllowedFailures = new List<Assertion>();
assertionFailures = new List<Assertion>();
}
/// <summary>
/// Records assertion failures.
/// </summary>
/// <param name="message"></param>
/// <param name="detailMessage"></param>
public override void Fail(string message, string detailMessage)
{
var failure = new Assertion(message, detailMessage, GetAssertionMethodName());
if (!AllowedFailures.Contains(failure))
{
assertionFailures.Add(failure);
}
}
/// <summary>
/// Records assertion failures.
/// </summary>
/// <param name="message"></param>
public override void Fail(string message)
{
Fail(message, null);
}
/// <summary>
/// Gets rid of any assertions that have been recorded.
/// </summary>
public void ClearAssertions()
{
assertionFailures.Clear();
}
/// <summary>
/// Gets the full name of the method that causes the assertion failure.
///
/// Credit goes to John Robbins of Wintellect for the code in this method,
/// which was taken from his excellent SuperAssertTraceListener.
/// </summary>
/// <returns></returns>
private String GetAssertionMethodName()
{
StackTrace stk = new StackTrace();
int i = 0;
for (; i < stk.FrameCount; i++)
{
StackFrame frame = stk.GetFrame(i);
MethodBase method = frame.GetMethod();
if (null != method)
{
if(method.ReflectedType.ToString().Equals("System.Diagnostics.Debug"))
{
if (method.Name.Equals("Assert") || method.Name.Equals("Fail"))
{
i++;
break;
}
}
}
}
// Now walk the stack but only get the real parts.
stk = new StackTrace(i, true);
// Get the fully qualified name of the method that made the assertion.
StackFrame hitFrame = stk.GetFrame(0);
StringBuilder sbKey = new StringBuilder();
sbKey.AppendFormat("{0}.{1}",
hitFrame.GetMethod().ReflectedType.FullName,
hitFrame.GetMethod().Name);
return sbKey.ToString();
}
}
Vous pouvez ajouter des Affirmations AllowedFailures collection au début de chaque test pour les affirmations que vous attendez.
À la fin de chaque test (j'espère que votre framework de test unitaire prend en charge un test de permutation de la méthode) faire:
if (DebugAssertListener.AssertionFailures.Count > 0)
{
// TODO: Create a message for the failure.
DebugAssertListener.ClearAssertions();
DebugAssertListener.AllowedFailures.Clear();
// TODO: Fail the test using the message created above.
}