Сушиться с носорогом издевается


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

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)
    {
        ...
    }
}

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

Может ли кто-нибудь предложить лучший подход?
4   6   2010-01-29 01:27:42

4 ответа:

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

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

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

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

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

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

Вот недавний образец устройства тест:

[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 ). создавайте конструкторы и используйте их в тестах вместо всевозможных установочных материалов.

Как:

    //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, потому что я хочу точно видеть, какой тип моих объектов, я предпочел бы пропустить имя типа в шаблоне.

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