Является ли использование атрибута NUnit Sequential правильной стратегией для достижения одной проверки за тест? - PullRequest
1 голос
/ 24 июня 2009

Предположим, у меня есть несколько небольших тестовых случаев. Достаточно прост в настройке и достаточно прост в исполнении.
Предположим, что каждый маленький тестовый набор может выполнить несколько проверок.

Поскольку я согласен с тем фактом, что один тестовый случай должен в идеале проверить одно условие , я полагаю, что у меня есть несколько вариантов реализации такого требования.

  1. Создание базового класса со всеми настройками и выполнением частей тестовых примеров. Каждый класс-потомок выполняет проверку одного требования. (решение 1)
  2. Храните все в одном классе, напишите методы проверки для каждого требования и позвольте каждому методу проверки вызывать общую процедуру настройки / упражнения. (решение 2)
  3. (ab) Используйте атрибут NUnit Sequential, чтобы выполнить один тест для одного условия. (решение 3)

Поскольку мне не очень нравится решение 1 или 2, потому что оно требует от вас lookup того, что делает часть теста по настройке и упражнениям, вы бы классифицировали использование атрибута Sequential NUnit в таком сценарии, чтобы запах или раствор.


Оригинал: несколько проверок / метод испытаний

[Test]
public void MyTest1()
{
    // Setup
    IMyObject object1 = new MyObject(/* Parameter combination X */)
    this.myObjectList.Add(object1);
    // Exercise
    result = this.LPSolve(this.myObjectList);
    // Check
    Assert.AreSame(null, result.Object);
    Assert.AreEqual(-1, result.Number);
    Assert.True(result.Messages.Contains(ErrorMessage1));
}

[Test]
public void MyTest2()
{
    // Setup
    IMyObject object1 = new MyObject(/* Parameter combination Y */)
    IMyObject object2 = new MyObject(/* Parameter combination Y */)
    this.myObjectList.Add(object1);
    this.myObjectList.Add(object2);
    // Exercise
    result = this.LPSolve(this.myObjectList);
    // Check
    Assert.AreSame(object2, result.Object);
    Assert.AreEqual(8, result.Number);
    Assert.True(result.Messages.Contains(ErrorMessage1));
    Assert.True(result.Messages.Contains(ErrorMessage2));
}

Решение 1: базовый класс с кодом настройки и превышения

[Test]
public virtual void MyTest1()
{
    // Setup
    IMyObject object1 = new MyObject(/* Parameter combination X */)
    this.myObjectList.Add(object1);
    // Exercise
    result = this.LPSolve(this.myObjectList);
    // Check
    MyTest1Check();
}

[Test]
public virtual void MyTest2()
{
    // Setup
    IMyObject object1 = new MyObject(/* Parameter combination Y */)
    IMyObject object2 = new MyObject(/* Parameter combination Y */)
    this.myObjectList.Add(object1);
    this.myObjectList.Add(object2);
    // Exercise
    result = this.LPSolve(this.myObjectList);
    // Check
    MyTest2Check();
}
...
/* Class ObjectCheck */
/* Check result object */
public override MyTest1Check()
{
   Assert.AreSame(null, this.result.Object);        
}

public override MyTest2Check()
{
   Assert.AreSame(this.myObjectList[1], this.result.Object);        
}
...
/* Class NumberCheck */
/* Check result number */
public override MyTest1Check()
{
   Assert.AreSame(-1, this.result.Number);        
}

public override MyTest2Check()
{
   Assert.AreSame(8, this.result.Number);        
}
...
/* and so on */

Решение 2: методы тестирования, вызывающие методы настройки / упражнения

public void SetupMyTest1()
{
    // Setup
    IMyObject object1 = new MyObject(/* Parameter combination X */)
    this.myObjectList.Add(object1);
    // Exercise
    this.result = this.LPSolve(this.myObjectList);
}

[Test]
public void MyTest1ObjectShouldBeNull()
{
    SetupMyTest1();
    // Check
    Assert.AreSame(null, this.result.Object);
}

[Test]
public void MyTest1ResultShouldBeMinus1()
{
    SetupMyTest1();
    // Check
    Assert.AreEqual(-1, this.result.Number);
}

[Test]
public void MyTest1MessageShouldContainErrorMessage1()
{
    SetupMyTest1();
    // Check
    Assert.True(this.result.Messages.Contains(ErrorMessage1));
}

public void SetupMyTest2()
{
    // Setup
    IMyObject object1 = new MyObject(/* Parameter combination Y */)
    IMyObject object2 = new MyObject(/* Parameter combination Y */)
    this.myObjectList.Add(object1);
    this.myObjectList.Add(object2);
    // Exercise
    this.result = this.LPSolve(this.myObjectList);
}

[Test]
public void MyTest2ObjectShouldBeObject2()
{
    SetupMyTest2();
    // Check
    Assert.AreSame(this.myObjectList[1], this.result.Object);
}

[Test]
public void MyTest2ResultShouldBeEight()
{
    SetupMyTest2();
    // Check
    Assert.AreEqual(8, this.result.Number);
}

[Test]
public void MyTest2MessageShouldContainErrorMessage1()
{
    SetupMyTest2();
    // Check
    Assert.True(this.result.Messages.Contains(ErrorMessage1));
}

[Test]
public void MyTest2MessageShouldContainErrorMessage2()
{
    SetupMyTest2();
    // Check
    Assert.True(this.result.Messages.Contains(ErrorMessage2));
}

Решение 3: использование атрибутов хранит настройку / упражнение / проверку в одном месте и выполняет одну проверку / проверку (оба метода выполняются 3 и 4 раза соответственно)

[Test, Sequential]
public void MyTest1([Values("CheckObject", "CheckNumber", "CheckErrorMessage1") string check)
{
    // Setup
    IMyObject object1 = new MyObject(/* Parameter combination X */)
    this.myObjectList.Add(object1);
    // Exercise
    result = this.LPSolve(this.myObjectList);
    // Check
    if (check == "CheckObject") Assert.AreSame(null, result.Object);
    else if (check = "CheckNumber") Assert.AreEqual(-1, result.Number);
    else if (check = "CheckErrorMessage1") Assert.True(result.Messages.Contains(ErrorMessage1));
    else throw new ArgumentOutOfRangeException(check);
}

[Test, Sequential]
public void MyTest2([Values("CheckObject", "CheckNumber", "CheckErrorMessage1", "CheckErrorMessage2") string check)
{
    // Setup
    IMyObject object1 = new MyObject(/* Parameter combination Y */)
    IMyObject object2 = new MyObject(/* Parameter combination Y */)
    this.myObjectList.Add(object1);
    this.myObjectList.Add(object2);
    // Exercise
    result = this.LPSolve(this.myObjectList);
    // Check
    if (check == "CheckObject") Assert.AreSame(object2, result.Object);
    else if (check = "CheckNumber") Assert.AreEqual(8, result.Number);
    else if (check = "CheckErrorMessage1") Assert.True(result.Messages.Contains(ErrorMessage1));
    else if (check = "CheckErrorMessage2") Assert.True(result.Messages.Contains(ErrorMessage2));
    else throw new ArgumentOutOfRangeException(check);
}

1 Ответ

1 голос
/ 09 июля 2009

Мне кажется, что ваши первые два модульных теста достаточно просты для понимания и их можно оставить в покое. В данном модульном тесте вы хотите проверить одну «единицу» функциональности, которая не обязательно представлена ​​в одном утверждении.

В исходном коде посмотрите на MyTest1 (). Мне кажется, что вы используете недопустимые параметры, и возвращается ошибка. Я обычно называю модульный тест чем-то вроде LPSolveReturnsErrorWithInvalidParameters (). Это определяет один блок для тестирования. Чтобы доказать, что эта функциональность завершена, необходимо выполнить три требования к выходным данным

  1. Свойство Object должно быть нулевым
  2. Для свойства Number установлено значение -1
  3. Коллекция сообщений содержит соответствующую информацию об ошибке

Представляется разумным иметь эти три подтверждения в одном модульном тесте. Другие потенциальные решения, которые вы предлагаете, кажутся слишком сложными и ненужными

...