2017-01-18 2 views
1

Рассмотрим этот код:коллекция Тест не содержит несколько членов

const string user8 = "user8"; 
const string user9 = "user9"; 
string[] users = { "user1", "user2", "user3", "user4", user8 }; 

Я хочу, чтобы проверить, что users не содержит ни user8 или User9. Раньше я использовал

Assert.That(users, Is.Not.SupersetOf(new[] {user8, user9 })); 

К сожалению, он проходит тест (что не ожидается). Я могу использовать

Assert.That(users, Does.Not.Contains(user8).And.Not.Contains(user9)); 

Но это будет проблематично, если я намерен протестировать коллекцию с более чем двумя членами. Есть ли лучший синтаксис? Я использую NUnit 3.4.

Примечание: цель заключается не только в результатах теста, но и в правильном утверждении, поэтому всякий раз, когда тест терпит неудачу, мы можем быстрее определить сообщение об ошибке. Это пример сообщения об ошибке из предыдущего примера (с использованием Does.Not.Contains)

«Ожидаемый: не коллекция, содержащая„user8“и не содержащим„User9“ Но была: <„user1“,„user2“,„user3“,» user4" , "user8">»

+0

Вы пробовали 'CollectionAssert'? – Hendry

+0

'Assert.That (new [] {user8Name, user9Name}, Is.Not.SubSetOf (пользователи));' – Nkosi

+0

@Hendry существует несколько методов в 'CollectionAssert'. Вы можете уточнить? FYI, я предпочитаю использовать [ограничение] (https://github.com/nunit/docs/wiki/Constraints). Но это единственное решение, тогда это не должно быть проблемой. – iroel

ответ

0

Порывшись о создании пользовательских ограничений и загрузки NUnit исходный код, так что я решил создать собственный CollectionContainsConstraint

/// <summary> 
/// CollectionContainsAnyConstraint is used to test whether a collection 
/// contains any member in expected collection. 
/// </summary> 
/// <typeparam name="T"></typeparam> 
public class CollectionContainsAnyConstraint<T> : CollectionContainsConstraint 
{ 
    public CollectionContainsAnyConstraint(IEnumerable<T> expected) : base(expected) 
    { 
    } 

    public override string Description 
     => Regex.Replace(base.Description, @"^\s*collection containing", "collection containing any of"); 

    /// <summary> 
    /// Test whether any member in expected collection is available in actual collection 
    /// </summary> 
    /// <param name="actual">Actual collection</param> 
    /// <returns></returns> 
    protected override bool Matches(IEnumerable actual) 
    { 
     var convertedExpected = (IEnumerable<T>)Expected; 
     var convertedActual = EnsureHasSameGenericType(actual, typeof(T)); 
     return convertedActual.Any(x => convertedExpected.Contains(x)); 
    } 

    private IEnumerable<T> EnsureHasSameGenericType(IEnumerable actual, Type expectedType) 
    { 
     var sourceType = actual.GetType(); 
     var sourceGeneric = sourceType.IsArray 
      ? sourceType.GetElementType() 
      : sourceType.GetGenericArguments().FirstOrDefault(); 
     if (sourceGeneric == null) 
      throw new ArgumentException("The actual collection must contain valid generic argument"); 
     if (!sourceGeneric.Equals(expectedType)) 
      throw new ArgumentException($"The actual is collection of {sourceGeneric.Name} but the expected is collection of {expectedType.Name}"); 
     return (IEnumerable<T>)actual; 
    } 
} 

public static class ConstraintExtensions 
{ 
    /// <summary> 
    /// Returns a new <see cref="CollectionContainsAnyConstraint{T}"/> checking for the 
    /// presence of any object of the <see cref="expected"/> collection against actual collection. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="expression"></param> 
    /// <param name="expected">A collection where one of its member is available in actual collection</param> 
    /// <returns></returns> 
    public static CollectionContainsAnyConstraint<T> ContainsAny<T>(this ConstraintExpression expression, 
     params T[] expected) 
    { 
     var constraint = new CollectionContainsAnyConstraint<T>(expected); 
     expression.Append(constraint); 
     return constraint; 
    } 
} 

и использование будет выглядеть следующим образом

[TestFixture] 
class GivenCustomCollectionContainsTest 
{ 
    const string User8 = "user8"; 
    const string User9 = "user9"; 
    private readonly List<string> users = new List<string> { "user1", "user2", "user3", "user4", "user5" }; 

    [Test] 
    public void WhenActualContainsOneOfExpectedAndPreceededByNotOperatorThenItShouldFail() 
    { 
     var actual = users.ToList(); 
     actual.Add(User8); 

     var assert = Assert.Throws<AssertionException>(() => Assert.That(actual, Does.Not.ContainsAny(User8, User9))); 
     Assert.That(assert.ResultState.Status, Is.EqualTo(TestStatus.Failed)); 
     Assert.That(assert.Message, Does.Contain("not collection containing any of").And.Contain($"\"{User8}\"")); 
    } 

    [Test] 
    public void WhenActualContainsAllOfExpectedAndPreceededByNotOperatorThenItShouldFail() 
    { 
     var actual = users.ToList(); 
     actual.Add(User8); 
     actual.Add(User9); 

     var assert = Assert.Throws<AssertionException>(() => Assert.That(actual, Does.Not.ContainsAny(User8, User9))); 
     Assert.That(assert.ResultState.Status, Is.EqualTo(TestStatus.Failed)); 
     Assert.That(assert.Message, Does.Contain("not collection containing any of").And.Contain($"\"{User8}\", \"{User9}\"")); 
    } 
} 

Просто отметьте, что это предназначено только для отрицательного теста. К счастью, мне не нужен тест, который «любой член коллекции доступен в другой коллекции» на данный момент. В основном, тесты для коллекции являются частью другой коллекции, поэтому я могу использовать Is.SupersetOf.

0

Попробуйте использовать CollectionAssert.IsNotSubsetOf()

CollectionAssert.IsNotSubsetOf(new[] {user8Name, user9Name }), users); 

Update:

Ну, вы всегда можете использовать основные л ooping.

Array.ForEach(new[] { user8, user9 }, u => Assert.That(users, Has.No.Member(u))); 

Это будет проверять users содержит ли какие-либо экземпляры в new[] { user8, user9 } массиве, обернув его.

Сообщение об ошибке будет что-то вроде этого:

Ожидаемое: не коллекция, содержащая "user8" Но был: < "user1", "user8", "user2", "User3">

+0

Сценарий ожидает отказа, потому что он содержит user8 – iroel

+0

обновил ответ. – Hendry

+0

'boolean' тестирование всегда работает, хотя в этом состоянии он не присваивается, потому что всякий раз, когда тест терпит неудачу, он генерирует неинформативное сообщение. Спасибо за ответ в любом случае. – iroel

0

Попробуйте проверить список исключений в отношении пользователей

const string user8 = "user8"; 
const string user9 = "user9"; 
string[] users = { "user1", "user2", "user3", "user4", user8 }; 

Assert.Multiple(() => { 
    var exclude = new[] { user8, user9 }; 
    foreach(var user in exclude) { 
     Assert.That(users, Has.No.Member(user)); 
    } 
} 

Этот тест не потому, что список пользователей действительно содержит user8

NUnit Docs - CollectionContainsConstraint

+0

В этом случае тест должен завершиться неудачно, потому что 'users' содержит' user8'. – iroel

+0

как следует. вы указали «Я хочу проверить, что пользователи не содержат пользователя user8 или user9», что означает, что тест должен завершиться неудачно, если пользователь8 находится в списке пользователей. Уточните, что вы хотите. – Nkosi

+0

Да, это тестовое ожидание. Целью является поиск встроенного синтаксиса, поэтому сообщение об ошибке также будет иметь соответствующее сообщение. Это будет работать в большинстве случаев. Можно ли написать собственное ограничение, содержащее этот код? – iroel

Смежные вопросы