2

我创建了一个从 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 集合没有更新以反映这一点。但是,在调试实际应用程序时,始终会在 .DataSource 更改时更新 .Items 集合。

当然,我不必在测试中实际渲染 ComboBox,我什至没有设置任何绘图表面来渲染!也许我需要的唯一答案是“在我实际上不需要绘制框的单元测试场景中,如何以与绘制时相同的方式更新组合框?”

4

5 回答 5

2

由于您只是调用构造函数,因此组合框的许多功能将不起作用。例如,当 ComboBox 在屏幕上或表单上绘制时,项目将被填充。在单元测试中构建它时不会发生这种情况。

为什么要在该组合框上编写单元测试?

您不能将现在在自定义控件中的逻辑分开吗?例如把它放在一个控制器中,然后测试它?

为什么不对 DataSource 属性而不是 Items 集合进行测试?

于 2009-01-26T12:16:09.013 回答
0

我确信Application.Run缺席不会影响任何控件的行为

于 2009-01-26T12:13:14.273 回答
0

我在项目绑定数据的组合框中遇到了同样的问题。我目前的解决方案是在测试中创建一个表单,将组合框添加到 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);
    }
于 2009-02-20T06:35:38.153 回答
0

如果目标是 ComboBox 或任何其他控件,这将解决一些问题:

目标.CreateControl();

但我无法将 SelectedValue 设置为空值,我的测试使用组合框的两个数据源,一个作为数据源,第二个绑定到 selevted 值。使用其他控件一切正常。一开始我也在测试中创建表单,但是在执行测试时在我们的构建服务器上创建表单时出现问题。

于 2009-03-31T13:47:29.227 回答
0

我做了一些小技巧以在我的自定义派生组合框中允许这样做:

public class EnhancedComboBox : ComboBox 
{

    [... the implementation]

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

SetItemsCore函数指示基本组合框使用提供的列表加载内部项目,这是数据源更改后内部使用的内容。

当控件不在表单上时,这个函数永远不会被调用,因为有很多对CurrencyManagers 和BindingContexts 的检查都失败了,因为我相信这个组件是由父表单以某种方式提供的。

无论如何,在测试中,您必须在mECB.DoRefreshItems()the 之后调用mECB.DataSource = mTestItems,如果您只依赖SelectedIndex andItems属性,一切都应该没问题。像数据绑定这样的任何其他行为可能仍然不起作用。

于 2012-05-03T17:00:47.373 回答