СУХОЕ с Rhino Mocks

Я ищу способы сделать следующее более кратким.

public class MyTests
{
    IPresenter presenter;

    [SetUp]
    public void SetUp()
    {
        presenter = MockRepository.GenerateStub<IPresenter>();
    }

    ...
}

В частности, повторное указание типа при создании макета представляется излишним. Например, я могу написать это так и использовать отражение, чтобы получить тип и автоматически создать заглушку:

public class MyTests
{
    IPresenter presenter;

    [SetUp]
    public void SetUp()
    {
        Stub(x => x.presenter);
    }

    void Stub(Expression<Func<MyTests, object>> expression)
    {
        ...
    }
}

Это будет работать, но компилятор больше не сможет определить, назначен ли докладчик, и начнет выдавать предупреждения. Это также делает ReSharper очень несчастным.

Кто-нибудь может предложить лучший подход?

4 ответа

Решение

Это может быть спорным, но я предпочитаю удобочитаемость, а не DRY-ness* в модульных тестах.

Другими словами, методы настройки отсутствуют в моих модульных тестах. Они используются только для интеграционных тестов. Я считаю, что XUnit.NET также занимает эту позицию.

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

** Естественно, мои юнит-тесты, скажем, в среднем на десять строк, если это увеличивается или объем настройки теста (в соответствии с AAA - Arrange, Act Assert) велик, только тогда я уберу дублирование и создаю вспомогательные методы. Чтобы прояснить этот момент, для более чистых тестов вы можете создать базовый класс тестирования, который содержит вспомогательные методы и другой код настройки.*

Да не пользуюсь [Setup] и переменные-члены вообще, вместо этого пишите объекты Fixture с методами создания.

Объект Fixture будет просто содержать соответствующий макет и другие части Fixture.

Я лично использую AutoFixture в качестве объекта Fixture и настроил его в качестве Auto-Mocking Container для загрузки, поэтому мне не нужно писать никакой фиктивный код, если мне не нужно явно определить какое-либо поведение.

Вот недавний примерный тестовый модуль:

[TestMethod]
public void DeleteProductWillDeleteProductFromRepository()
{
    // Fixture setup
    var fixture = new ServiceFixture();
    var id = fixture.CreateAnonymous<int>();
    var repMock = fixture.FreezeMoq<ProductRepository>();

    var sut = fixture.CreateAnonymous<ProductManagementService>();
    // Exercise system
    sut.DeleteProduct(id);
    // Verify outcome
    repMock.Verify(r => r.DeleteProduct(id));
    // Teardown
}

В этом случае, repMock создан Moq, но я мог бы настроить его на использование Rhino Mocks.

У Майкла Фезерса есть выдающийся взгляд на это (см. Его презентацию http://www.ndc2010.no/index.aspx?id=361621). Создайте Builders и используйте это в тестах вместо всевозможных настроек.

Подобно:

    //The Class to Test
    public class ObjectY
    {
        public string DoThis(IObjectX objectX)
        {
            return objectX.id + objectX.name;
        }
    }


    [Test]
    //The test
    public void CreaeteTestData()
    {
        //Almost prosa style creation of test data 
        var testData = new ObjectXBuilder().WithId(123).WithName("ABC").Build();

        Assert.That(new ObjectY().DoThis(testData), Is.EqualTo("123ABC"));

    }


    //The Builder class - Provides easy creation testdata.
    internal class ObjectXBuilder
    {
        private MockRepository _mockRepository;
        private IObjectX _objectX;

        public ObjectXBuilder()
        {
            _mockRepository = new MockRepository();
            _objectX = _mockRepository.Stub<IObjectX>();
        }

        public ObjectXBuilder WithName(string name)
        {
            _objectX.name = name;
            return this;
        }

        public ObjectXBuilder WithId(long id)
        {
            _objectX.id = id;
            return this; 
        }

        public IObjectX Build()
        {
            _mockRepository.ReplayAll();
            return _objectX;
        }

    }

Общая проблема с C# в том, что он не выводит тип из результата метода (в отличие от Java), и это болезненно во многих ситуациях (просто приведу другой пример, где вы хотите реализовать метод десериализации). Лично мне не нравится использовать ключевое слово var, потому что я хочу точно знать, какой тип моих объектов, я бы предпочел пропустить имя типа в шаблоне.

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

Другие вопросы по тегам