【问题标题】:unit testing a class with event and delegate使用事件和委托对类进行单元测试
【发布时间】:2013-05-08 20:00:58
【问题描述】:

我是测试新手,请帮忙。

我有以下课程

public delegate void OnInvalidEntryMethod(ITnEntry entry, string message);

public class EntryValidator
{
    public event OnInvalidEntryMethod OnInvalidEntry;

    public bool IsValidEntry(ITnEntry entry, string ticker)
    {
        if (!IsFieldValid(entry, ticker.Trim().Length.ToString(), "0"))
            return false;

        return true;
    }

    private bool IsFieldValid(ITnEntry entry, string actual, string invalidValue)
    {
        if (actual == invalidValue)
        {
            RaiseInvalidEntryEvent(entry);
            return false;
        }

        return true;
    }

    private void RaiseInvalidEntryEvent(ITnEntry entry)
    {
        if (OnInvalidEntry != null)
            OnInvalidEntry(entry, "Invalid entry in list: " + entry.List.Name + ".");
    }
}

到目前为止,我已经编写了测试用例,但正在努力处理事件和委托,如下所示

[TestFixture]
public class EntryValidatorTests
{
    private EntryValidator _entryValidator;

    private FakeTnEntry _selectedEntry;
    private string _ticker;

    [SetUp]
    public void Setup()
    {
        _entryValidator = new EntryValidator();
        _ticker = "BOL";
    }

    private FakeTnEntry MakeEntry(string ticker)
    {
        return new FakeTnEntry { Ticker = ticker};
    }

    [Test]
    public void IsValidEntry_WithValidValues()
    {
        _selectedEntry = MakeEntry(_ticker);

        Assert.IsTrue(_entryValidator.IsValidEntry(_selectedEntry, _selectedEntry.Ticker));
    }

    [Test]
    public void IsValidEntry_WithInValidTicker()
    {
        _selectedEntry = MakeEntry("");
        Assert.IsFalse(_entryValidator.IsValidEntry(_selectedEntry, _selectedEntry.Ticker));
    }
}}

请问有人可以帮忙吗?谢谢..

【问题讨论】:

    标签: c# unit-testing events delegates nunit


    【解决方案1】:

    使用匿名方法订阅事件可能是最简单的:

    [Test]
    public void IsValidEntry_WithValidValues()
    {
        _selectedEntry = MakeEntry(_ticker);
        _entryValidator.OnInvalidEntry += delegate { 
            Assert.Fail("Shouldn't be called");
        };
    
        Assert.IsTrue(_entryValidator.IsValidEntry(_selectedEntry, _selectedEntry.Ticker));
    }    
    
    [Test]
    public void IsValidEntry_WithInValidTicker()
    {
        bool eventRaised = false;
        _selectedEntry = MakeEntry("");
        _entryValidator.OnInvalidEntry += delegate { eventRaised = true; };
    
        Assert.IsFalse(_entryValidator.IsValidEntry(_selectedEntry, _selectedEntry.Ticker));
        Assert.IsTrue(eventRaised);
    }
    

    在第二个测试中,您可能想要验证事件参数是否也符合预期。

    还要注意“无效”是一个词 - 所以你的测试应该是IsValidEntry_WithInvalidTicker。我也不会为设置而烦恼 - 我只需在每个测试中声明新的局部变量。

    【讨论】:

    • 非常感谢先生的快速回复。我不需要 OnInvalidEntry 的属性或事件吗?
    • @user175084:您已经在EntryValidator得到该事件。您只是使用匿名方法订阅事件。
    • 非常感谢//是否有文章或样本或任何类型的材料我可以阅读更多关于此的内容?
    • @user175084:哪个位?匿名方法?你熟悉 lambda 表达式吗?
    • 关于测试的好文章/教程,不,我对 lambda 表达式不太熟悉,但对它有所了解。
    【解决方案2】:

    我将重组您的课程以使 RaiseInvalidEntryEvent 虚拟化,以便可以在您的 IsValidEntry_WithInValidTicker 中模拟它,然后验证它在票证无效时被调用。

    然后我会进行另一个测试,验证 RaiseInvalidEntryEvent 分别调用匿名委托。

    单元测试应尽可能原子化,并且您可能希望在不同的测试中验证这两种行为。

    public delegate void OnInvalidEntryMethod(ITnEntry entry, string message);
    
    public class EntryValidator
    {
        public event OnInvalidEntryMethod OnInvalidEntry;
    
        public bool IsValidEntry(ITnEntry entry, string ticker)
        {
            if (!IsFieldValid(entry, ticker.Trim().Length.ToString(), "0"))
                return false;
    
            return true;
        }
    
        private bool IsFieldValid(ITnEntry entry, string actual, string invalidValue)
        {
            if (actual == invalidValue)
            {
                RaiseInvalidEntryEvent(entry);
                return false;
            }
    
            return true;
        }
    
        public virtual void RaiseInvalidEntryEvent(ITnEntry entry)
        {
            if (OnInvalidEntry != null)
                OnInvalidEntry(entry, "Invalid entry in list: " + entry.List.Name + ".");
        }
    }
    
    // Had to reverse engineer the following since they were not available in the question
    public interface ITnEntry
    {
        Ticket List { get; set; }
        string Ticker { get; set; }
    }
    
    public class TnEntry : ITnEntry
    {
        public Ticket List { get; set; }
        public string Ticker { get; set; }
    }
    
    public class Ticket
    {
        public string Name { get; set; }
    }
    

    注意:当事情被宣布为公开而不是私有时,一些 OOP 传播者会适应,基本上单元测试和 TDD 有一些纯 OOP 不符合的要求。为简单起见,我将 RaiseInvalidEntryEvent 公开,但通常我会将其设为内部,然后通过 InternalsVisibleTo 将程序集公开给单元测试。过去 4 年我一直在做 TDD,现在很少使用 private 了。

    单元测试很快就会完成(注意,这是使用 VS2012 的 MSTEST 框架)

    [TestClass]
    public class UnitTest1
    {
        #region TestHelpers
    
        private ITnEntry MakeEntry(string ticker)
        {
            return new TnEntry {Ticker = ticker, List = new Ticket()};
        }
    
        #endregion
    
        [TestMethod]
        public void IsValidEntry_WithValidValues_ReturnsTrue()
        {
            // ARRANGE
            var target = new EntryValidator();
            var selectedEntry = MakeEntry("BOL");
    
            // ACT
            bool actual = target.IsValidEntry(selectedEntry, selectedEntry.Ticker);
    
            // ASSERT
            Assert.IsTrue(actual);
        }
    
        [TestMethod]
        public void IsValidEntry_WithInValidTicker_ReturnsFalse()
        {
            // ARRANGE
            var target = new EntryValidator();
            var selectedEntry = MakeEntry("");
    
            // ACT
            bool actual = target.IsValidEntry(selectedEntry, selectedEntry.Ticker);
    
            // ASSERT
            Assert.IsFalse(actual);
        }
    
        [TestMethod]        
        public void IsValidEntry_WithInvalidTicker_RaisesEvent()
        {
            // ARRANGE
            // generate a dynamic mock which will stub all virtual methods
            var target = Rhino.Mocks.MockRepository.GenerateMock<EntryValidator>();
            var selectedEntry = MakeEntry("");
    
            // ACT
            bool actual = target.IsValidEntry(selectedEntry, selectedEntry.Ticker);
    
            // ASSERT
            // assert that RaiseInvalidEntryEvent was called
            target.AssertWasCalled(x => x.RaiseInvalidEntryEvent(Arg<ITnEntry>.Is.Anything));
        }
    
        [TestMethod]
        public void RaiseInvalidEntryEvent_WithValidHandler_CallsDelegate()
        {
            // ARRANGE
            var target = new EntryValidator();
            var selectedEntry = MakeEntry("");
            bool delegateCalled = false;
    
            // attach a handler to set delegateCalled to true
            target.OnInvalidEntry += delegate 
            {
                delegateCalled = true;
            };
    
            // ACT
            target.IsValidEntry(selectedEntry, selectedEntry.Ticker);
    
            // ASSERT
            Assert.IsTrue(delegateCalled);
        }
    }
    

    【讨论】:

      【解决方案3】:

      您的测试应该使用虚拟方法订阅事件OnInvalidEntry,调用IsValidEntry 并检查结果。

      【讨论】:

        猜你喜欢
        • 2016-07-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多