Unit Testing возвращаемое значение метода, вызванного из другого метода - PullRequest
4 голосов
/ 26 января 2012

У меня есть метод, подобный этому:

public List<MyClass> DoSomething(string Name, string Address, string Email, ref string ErrorMessage)
{
  //Check for empty string parameters etc now go and get some data   
  List<MyClass> Data = GetData(Name, Address, Email);

  /*************************************************************    
  //How do I unit test that the data variable might be empty???    
  *************************************************************/

  List<MyClass> FormattedData = FormatData(Data);
  return FormattedData;
}

Я только учусь TDD / Unit Testing. Мой вопрос: как мне написать тест, чтобы гарантировать, что если GetData возвращает пустой список, я устанавливаю ErrorMessage на что-то, а затем возвращаю пустой список?

Ответы [ 4 ]

3 голосов
/ 26 января 2012

Модульное тестирование - это не то, что вы добавляете в середину существующих методов, оно касается тестирования небольших блоков кода изолированно от остальной системы, чтобы вы были уверены, что модуль ведет себя так, как должен.

Итак, вы должны написать второй класс, единственная ответственность которого заключается в том, чтобы проверить, что класс, в котором живет DoSomething (назовем этот класс Daddy и тестовый класс DaddyTests), ведет себя так, как вы ожидаете. Затем вы можете написать тестовый метод, который вызывает DoSomething и гарантирует, что ErrorMessage установлен правильно (также ErrorMessage должен быть параметром out, а не ref, если вы также не передаете значение в).

Чтобы упростить этот тест, вам нужно убедиться, что GetData не возвращает никаких данных. Обычно это можно сделать, передавая пустой набор данных в фальшивый провайдер, но в более сложных сценариях может потребоваться замена целых классов на фальшивые / фиктивные эквиваленты: использование интерфейсов и внедрение зависимостей делает эту задачу очень простой. (Обычно поставщик устанавливается во время создания Daddy, а не в качестве параметра при вызове DoSomething.)

public class Daddy {
    public List<MyClass> DoSomething(string Name, string Address,                  string Email, out string ErrorMessage, IDataProvider provider)
    {
      //Check for empty string parameters etc now go and get some data   
      List<MyClass> Data = provider.GetData(Name, Address, Email);

      if (Data.Count == 0)
      {
          ErrorMessage = "Oh noes";
          return Enumerable.Empty<MyClass>();
      }

      List<MyClass> formattedData = FormatData(Data);
      return formattedData;
    }
}

[TestClass]
public class DaddyTest {
    [TestMethod]
    public void DoSomethingHandlesEmptyDataSet() {
        // set-up
        Daddy daddy = new Daddy();

        // test
        IList<MyClass> result = daddy.DoSomething("blah",
                                                  "101 Dalmation Road",
                                                  "bob@example.com",
                                                  out error,
                                                  new FakeProvider(new Enumerable.Empty<AcmeData>())); // a class we've written to act in lieu of the real provider

        // validate
        Assert.NotNull(result); // most testing frameworks provides Assert functionality
        Assert.IsTrue(result.Count == 0);
        Assert.IsFalse(String.IsNullOrEmpty(error));
    }
}

}

2 голосов
/ 26 января 2012

При разработке вы должны были оставить «точку входа» для тестирования следующим образом:

public List<MyClass> DoSomething(string Name, string Address,
              string Email, ref string ErrorMessage, IDataProvider provider)
{
  //Check for empty string parameters etc now go and get some data   
  List<MyClass> Data = provider.GetData(Name, Address, Email);

  List<MyClass> FormattedData = FormatData(Data);
  return FormattedData;
}

А в модульном тестировании вы должны mock IDataProvider

Это называется Внедрение зависимостей (DI) или Инверсия управления (IOC)

общая библиотека насмешек:

Список был взят из здесь

Статьи Википедии:

Внедрение зависимостей
Инверсия управления

Псевдокод NUnit:

[Test]
public void When_user_forgot_password_should_save_user()
{
    // Arrange
    var errorMessage = string.Empty;
    var dataProvider = MockRepository.GenerateStub<IDataProvider>();
    dataProvider.Stub(x => x.GetData("x", "y", "z", ref errorMessage )).Return(null);

    // Act
    var result DoSomething("x","y","z", ref errorMessage, dataProvider);

    // Assert

    //...
}
1 голос
/ 26 января 2012

ИМО, линия

List<MyClass> Data = GetData(Name, Address, Email);

должно быть вне класса. С сигнатурой метода, измененной на

public List<MyClass> DoSomething(List<MyClass> data, ref string ErrorMessage)

этот метод становится намного проще для тестирования, так как вы можете легко изменять входы для проверки всех возможных крайних случаев.

Другой альтернативой является использование метода GetData в виде фиктивной зависимости, которую затем можно настроить для получения различных результатов. Итак, ваш класс теперь будет выглядеть так:

class ThisClass
{
   [Import]
   public IDataService DataService {get; set;}

   public List<MyClass> DoSomething(string Name, string Address, string Email, ref string ErrorMessage)
   {
     //Check for empty string parameters etc now go and get some data   
     List<MyClass> Data = IDataService.GetData(Name, Address, Email); // using dependency

     List<MyClass> FormattedData = FormatData(Data);
     return FormattedData;
   }
}
1 голос
/ 26 января 2012
[TestMethod]
public void MyDoSomethingTest()
{
   string errorMessage = string.Empty;
   var actual = myClass.DoSomething(..., ref errorMessage)
   Assert.AreEqual("MyErrorMessage", errorMessage);
   Assert.AreEqual(0, FormattedData.Count);
}

Я предполагаю, что если формат datato отсутствует, форматер вернет пустой список.

Поскольку вы хотите проверить окончательный результат метода, я бы не стал искатьиз того, что возвращено из функции GetData, поскольку это возвращаемое значение actaul, которое вы хотите проверить, что это пустой список и, возможно, что FormatData не вылетает.Вы можете проверить, является ли какой-либо из параметров пустым или пустым, и в этом случае просто наберите

errorMessage = "Empty parameters are not allowed";
return new List<MyClass>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...