Тестирование пользовательского элемента управления, полученного из ComboBox

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

Однако в моем модульном тесте он ведет себя иначе, чем в реальном приложении.

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

В моем тесте я создаю ComboBox, назначаю ему источник данных, но список.Items вообще не обновляется, оставаясь в 0 элементах. Таким образом, когда я пытаюсь обновить.SelectedIndex до 0 в тесте, чтобы выбрать первый элемент, я получаю ArgumentOutOfRangeException.

Это потому, что в моем модульном тесте нет запуска Application.Run, запускающего цикл обработки событий, или это немного красной сельди?

РЕДАКТИРОВАТЬ: Подробнее о первом тесте:

    [SetUp]
    public void SetUp()
    {
        mECB = new EnhancedComboBox();

        mECB.FormattingEnabled = true;
        mECB.Location = new System.Drawing.Point( 45, 4 );
        mECB.Name = "cboFind";
        mECB.Size = new System.Drawing.Size( 121, 21 );
        mECB.TabIndex = 3;

        mECB.AddObserver( this );

        mTestItems = new List<TestItem>();
        mTestItems.Add( new TestItem() { Value = "Billy" } );
        mTestItems.Add( new TestItem() { Value = "Bob" } );
        mTestItems.Add( new TestItem() { Value = "Blues" } );

        mECB.DataSource = mTestItems;
        mECB.Reset();

        mObservedValue = null;
    }

    [Test]
    public void Test01_UpdateObserver()
    {
        mECB.SelectedIndex = 0;
        Assert.AreEqual( "Billy", mObservedValue.Value );
    }

Тест не выполняется в первой строке при попытке установить SelectedIndex в 0. При отладке это происходит потому, что при изменении.DataSource коллекция.Items не обновляется, чтобы отразить это. Однако при отладке реального приложения коллекция.Items всегда обновляется при изменении.DataSource.

Конечно, мне не нужно визуализировать ComboBox в тесте, у меня даже не было настроено рисование поверхностей для рендеринга! Возможно, единственный ответ, который мне нужен, - "Как сделать обновление ComboBox таким же образом, как при рисовании, в сценарии модульного тестирования, где на самом деле мне не нужно рисовать прямоугольник?"

5 ответов

Решение

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

Почему вы хотите написать модульный тест для этого списка?

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

Почему бы вам не проверить свойство DataSource вместо коллекции Items?

Это решает некоторые проблемы, если целью является ComboBox или любой другой элемент управления:

target.CreateControl ();

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

У меня та же проблема с полем со списком, где элементы привязаны к данным. Мое текущее решение - создать форму в тесте, добавить поле со списком в коллекцию Controls, а затем показать форму в моем тесте. Вроде некрасиво. Все, что на самом деле делает мое поле со списком, - это список объектов TimeSpan, отсортированных и с пользовательским форматированием значений TimeSpan. Он также имеет особое поведение на событиях нажатия клавиш. Я пытался извлечь все данные и логику в отдельный класс, но не смог понять это. Возможно, есть лучшее решение, но то, что я делаю, кажется удовлетворительным.

Чтобы упростить тестирование, я создал эти классы в своем тестовом коде:

    class TestCombo : DurationComboBox {
        public void SimulateKeyUp(Keys keys) { base.OnKeyUp(new KeyEventArgs(keys)); }
        public DataView DataView { get { return DataSource as DataView; } }
        public IEnumerable<DataRowView> Rows() { return (DataView as IEnumerable).Cast<DataRowView>(); }
        public IEnumerable<int> Minutes() { return Rows().Select(row => (int)row["Minutes"]); }
    }

    class Target {
        public TestCombo Combo { get; private set; }
        public Form Form { get; private set; }

        public Target() {
            Combo = new TestCombo();
            Form = new Form();
            Form.Controls.Add(Combo);
            Form.Show();
        }
    }

Вот пример теста:

           [TestMethod()]
    public void ConstructorCreatesEmptyList() {
        Target t = new Target();
        Assert.AreEqual<int>(0, t.Combo.DataView.Count);
        Assert.AreEqual<int>(-1, t.Combo.SelectedMinutes);
        Assert.IsNull(t.Combo.SelectedItem);
    }

Я уверен в том что Application.Run отсутствие не может повлиять на поведение какого-либо контроля

Я сделал небольшой хак, чтобы разрешить это в своем собственном производном выпадающем списке:

public class EnhancedComboBox : ComboBox 
{

    [... the implementation]

    public void DoRefreshItems()
    {
        SetItemsCore(DataSource as IList);       
    }
}

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

Эта функция никогда не вызывается, когда элемент управления не находится в форме, потому что есть много проверок для CurrencyManagerс и BindingContexts, которые терпят неудачу, потому что эти компоненты, я полагаю, предоставляются родительской формой так или иначе

Во всяком случае, в тесте, вы должны позвонить mECB.DoRefreshItems() сразу после mECB.DataSource = mTestItems и все должно быть хорошо, если вы зависите только от SelectedIndex и Items имущество. Любое другое поведение, такое как привязка данных, вероятно, все еще не работает.

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