это правильный способ проверить докладчика - PullRequest
1 голос
/ 30 марта 2012

Я создаю торговый сайт с использованием веб-форм и шаблона MVP в трехслойной архитектуре.Я также решил провести валидацию и приведение типов внутри класса Presenter. Для среды тестирования я использую NUnit, а для своих издевательств - NSubstitude.вот мой класс модели категории:

    //we're doing Dependency injection here.
    public abstract class BaseRepository
    {
        EntityContext context;
        public BaseRepository()
        {
             context = new EntityContext();
        }
        public EntityContext Context
        {
             get { return context; }
        }
    }
    public class CategoryRepository : BaseRepository
    {
        public int Add(long id, string name)
        {
            Category cat = new Category();
            cat.Id = id;
            cat.Name = name;
            Context.Category.Add(cat);
            Context.SaveChanges();
        }
    }

вот ведущий категории:

    public class CategoryPresenter : BasePresenter //has nothing but a dependency property to Logger
    {
        BaseRepository _model;
        IView _view;
        public CategoryPresenter(IView view)
        {
            _model = new CategoryRepository();
            _view = view;
        }
        public void Add()
        {
            //havn't passed the tests yet since i'm not sure if i'm on the correct path.
            //whatever validation, loggin and type casting will go here.
            _model.Add(_view.CategoryId, _view.CategoryName);
        }
    }

и вот тестовый класс для ведущего:

    [Test]
    public void Add_NullId_ThrowException()
    {
        _view.CategoryId.Returns(p => null);
        _view.CategoryName.Returns(p => "test");
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }
    [Test]
    public void Add_EmptyId_ThrowException()
    {
        _view.CategoryId.Returns(p => "");
        _view.CategoryName.Returns(p => "test");
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }
    [Test]
    public void Add_SpaceOnlyId_ThrowException()
    {
        _view.CategoryId.Returns(p => " ");
        _view.CategoryName.Returns(p => "test");
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }
    [Test]
    public void Add_InvalidLowBoundId_ThrowException()
    {
        _view.CategoryId.Returns(p => "-1");
        _view.CategoryName.Returns(p => "test");
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }
    [Test]
    public void Add_InvalidHighBoundId_ThrowException()
    {
        _view.CategoryId.Returns(p => long.MaxValue.ToString() + "1");
        _view.CategoryName.Returns(p => "test");
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }

    [Test]
    public void Add_EmptyName_ThrowException()
    {
        _view.CategoryId.Returns(p => "1");
        _view.CategoryName.Returns(p => "");
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }
    [Test]
    public void Add_NullName_ThrowException()
    {
        _view.CategoryId.Returns(p => "1");
        _view.CategoryName.Returns(p => null);
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }
    [Test]
    public void Add_SpaceOnlyName_ThrowException()
    {
        _view.CategoryId.Returns(p => "1");
        _view.CategoryName.Returns(p => " ");
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }
    [Test]
    public void Add_NumberOnlyName_ThrowException()
    {
        _view.CategoryId.Returns(p => "1");
        _view.CategoryName.Returns(p => "123");
        Assert.Throws(typeof(InvalidOperationException), _presenter.Add());
    }

я правильно тестирую?я имею в виду, как должен выглядеть тестовый класс?я что-то упустил?это слишком много?например, «вам не нужно проверять пустоту» или какие-либо другие проблемы, связанные с моими тестами или кодом?Если вы заметили что-то не так в моем коде и / или архитектуре, я буду признателен, если вы меня поправите.спасибо!

Обновление: IView наследуется страницей .aspx.в коде позади я просто вызываю метод презентатора изнутри события щелчка, которое пользователь запускает нажатием кнопки.Что касается памяти, я еще не зашел так далеко.просто застрял на TDD.

1 Ответ

1 голос
/ 30 марта 2012

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

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

Для модульных тестов презентатора вы предоставляете презентаторам фиктивные объекты и проверяете, что для данных вызываются правильные методы проверки.

Таким образом, вы должны проверить две вещи: 1) Проверить, вызывает ли докладчик валидаторы с данными из представления. 2) Проверить валидаторы самостоятельно.

Тесты могут выглядеть следующим образом:

Для докладчика (класс CategoryPresenterTests):

[Test]
public void Add_CallsTheValidatorWithDataFromTheView()
{
    _viewMock.CategoryId.Returns(p => "id");
    _viewMock.CategoryName.Returns(p => "name");

    _presenter.Add();

    _categoryValidatorMock.Verify(x=>x.Validate("id", "name"), Times.Once);
}

[Test]
public void Add_ForwardsValidationExceptions()
{
    _viewMock.CategoryId.Returns(p => "id");
    _viewMock.CategoryName.Returns(p => "name");

    _categoryValidatorMock.Setup(x=>x.Validate(...)).Throws<ValidationException>();

    Assert.Throws<ValidationException>(() => _presenter.Add());
}

Обратите внимание, что мы не заботимся о конкретных входных данных из представления, только то, что валидатор вызывается с этими точными данными из представления и что результат (вэто исключение случая или без исключения) передается обратно.

Для валидатора (класс CategoryValidatorTests.В основном все ваши текущие тесты идут здесь):

[Test]
public void NullId_ThrowsException() {
  string id = null;
  string name = "test";
  Assert.Throws<ValidationException>(() => _validator.Validate(id, name));
}

Обратите внимание, что я не знаю синтаксис NSubstitutes, поэтому приведенный выше псевдокод .. надеюсь, вы можете расшифровать его:)

Кроме тогоЯ бы не создавал репозитории в презентаторах, вместо этого вставлял бы их интерфейсы через конструктор (как вы это делали с IView).Затем предоставьте фиктивные объекты и, как и в случае с валидаторами, убедитесь, что докладчик правильно вызвал их.

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

...