2015-11-18 2 views
4

Я пытаюсь внедрить webserviceclass с использованием подхода TDD, который отправляет кучу веб-запросов и интерпретирует ответы. Я инкапсулировал веб-запросы в нескольких интерфейсах, чтобы я мог легко их издеваться. При запросе чего-либо через webserviceclass реализованный метод всегда возвращает конкретный объект ответа, содержащий объект ошибки. С помощью этого объекта ошибки пользователь может определить, был ли запрос успешным или нет, и какова была конкретная ошибка.Как сохранить модульные тесты DRY и уменьшить Asserts

После написания кучи тестов, я понял, что я повторял себе много в фазе Упорядочить:

var mock = new Mock<ISomeWebservices>(); 
var sut = new MyWebServiceClass(mock.Object); 
mock.Setup(foo=>foo.SomeRequest(someData)).Returns(@"{""user"": ""12345"",""somedata"": ""60"",""someotherdata"":""2015-09-01T12:00:00.200Z""}"); 
sut.SomeRequest(someData,s=> response = s); 

Первые 2 строки всегда одинаков для всех тестов. Всегда существует программа установки для макета, которая возвращает что-то или генерирует исключение. В зависимости от того, какой запрос я тестирую, мне нужно настроить другой метод. Я попытался решить эту проблему, используя Autofixture, чтобы я мог написать ICustomization для каждого web-запроса, но проблема в том, что мне все еще нужен доступ к макету, чтобы иметь тестовую установку.

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

Assert.That(response.Error.Type,Is.EqualTo(ErrorInfo.ErrorType.IllegalToken)); 
    Assert.That(response.Error.Message, Is.Not.Null); 
    Assert.That(response.Error.AdvisedAction, Is.Not.Null); 
    Assert.That(response.Error.RawData, Is.Not.Null); 

Так что, если есть ошибка, есть некоторые свойства объекта ошибки, которые всегда должны быть заполнены, и некоторые из них могут быть. (т. е. если ошибка была вызвана исключением, свойство исключения должно быть не равным нулю и т. д.) Насколько я понял, наличие нескольких тестов в единичном тесте является плохой практикой, поэтому я хотел бы избежать этого, если это возможно.

[править] Согласно комментарии, имеющие множественные Утверждает, а также повторение аранжировать части I упоминает, не так уж плохо. Поэтому я не буду использовать Autofixture для этого.

+0

Сухость не так важна при написании Unit Tests, так как это может привести к хрупким испытаниям http://codebetter.com/karlseguin/2009/09/12/unit-testing-do-repeat-yourself/ – juharr

+0

Я не знаю, Думаю, несколько утверждений плохие. Тестирование разных, несвязанных вещей плохое. Но возвращение объекта и проверка правильности свойств для меня в порядке. –

+0

Для вашего sut, не можете ли вы иметь переменную класса и использовать метод установки для ее создания? –

ответ

4

Вы, безусловно, можете использовать AutoFixture, чтобы уменьшить количество кода шаблона, который вам нужен, в вашем Упорядочить фазу. Это может выглядеть следующим образом:

[Theory, AutoMoqData] 
public void Test(
    [Frozen]Mock<ISomeWebservices> mock, 
    MyWebServiceClass sut, 
    object someData, 
    object response) 
{ 
    mock.Setup(foo => foo.SomeRequest(someData)).Returns(@"{""user"": ""12345"",""somedata"": ""60"",""someotherdata"":""2015-09-01T12:00:00.200Z""}"); 
    sut.SomeRequest(someData, s => response = s); 
    // Assertions go here... 
} 

Здесь я должен был догадаться по типу someData и response, но если они отличаются от object, вы просто объявить их быть такого типа вместо.

В приведенном выше примере атрибут [AutoMoqData] определяется следующим образом:

public class AutoMoqDataAttribute : AutoDataAttribute 
{ 
    public AutoMoqDataAttribute() : 
     base(new Fixture().Customize(new AutoMoqCustomization())) 
    { 
    } 
} 

Все остальные типы и особенности AutoFixture взяты из AutoFixture.Xunit2 и AutoFixture.AutoMoq пакетов NuGet.

Когда дело доходит до нескольких утверждений, я согласен с другими комментаторами здесь, что это не выглядит так уж плохо, но в духе GOOS, вы должны слушать ваши тесты. В этом конкретном случае тест, кажется, говорит: сравнение равенства для response выглядит так, как будто это важно. Если это так, то можете рассмотреть возможность переустановки Equals на response и дать ему Структурное соглашение вместо Ориентировочное равенство.

+0

Спасибо за подробное объяснение, это актуально то, что я искал. Однако я использую Nunit, а не Xunit, но я не понимаю, почему это не должно работать с пакетом AutoFixture.Nunit2? Когда я пытаюсь это сделать, я получаю System.Reflection.TargetParameterCountException. – zlZimon

+0

@zlZimon Модель расширения NUnit 2 настолько перепутана, что вам повезет, что она работает. * AutoFixture.NUnit2 * был попыткой подключения порта * AutoFixture.Xunit2 * к NUnit, но AFAICT, это довольно flaky. Сделайте себе одолжение и вместо этого используйте xUnit.net. Достаточно легко перейти от NUnit к xUnit.net. –

+0

Я последовал твоему совету и перешел на XUnit2, и мне это очень нравится. К несчастью, я сильно использовал категорию NUnit, но это другая история. Мне было интересно, правильно ли всегда инициализировать sut/mocks/или любые данные, используемые в тесте, через атрибут Theory? Мне очень нравится, насколько чисты мои тесты. – zlZimon

3

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

В целом используют сборщики тестовых данных для упрощения и устранения дублирования при создании тестовых данных & объектов. Это также помогает, если вы вносите изменения в то, как создаются объекты, поскольку вы обновляете только один метод построения, а не все отдельные тесты.

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

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