Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unit testing a class with event and delegate

I am new to testing please help.

I have the following class

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 + ".");
    }
}

I have written the test case so far but am struggling with the event and delegate as shown below

[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));
    }
}}

Please can someone help? Thanks..

like image 790
user175084 Avatar asked May 08 '13 20:05

user175084


3 Answers

It's probably simplest just to subscribe to the event using an anonymous method:

[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);
}

In the second test you might want to validate that the event arguments were as expected too.

Also note that "invalid" is one word - so your test should be IsValidEntry_WithInvalidTicker. I'd also not bother with the setup - I'd just declare new local variables in each test.

like image 175
Jon Skeet Avatar answered Oct 17 '22 23:10

Jon Skeet


I would restructure your class to make the RaiseInvalidEntryEvent virtual so it can be mocked in your IsValidEntry_WithInValidTicker and then verified it was called when the ticket was invalid.

Then I would have another test that verified RaiseInvalidEntryEvent called the anon delegate separately.

Unit tests should be as atomic as possible, and you would want to verify both of these behaviors in different tests.

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; }
}

NOTE: Some OOP evangalists have fits when things are declared public instead of private, basically unit testing and TDD have some requirements that pure OOP is at odds with. I've made RaiseInvalidEntryEvent public for simplicity, but normally I would make this internal and then expose the assembly to the unit test via InternalsVisibleTo. I've been doing TDD for the last 4 years now and rarely use private anymore.

And the unit tests would quickly be (note, this is using the MSTEST framework from VS2012)

[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);
    }
}
like image 33
nrjohnstone Avatar answered Oct 17 '22 23:10

nrjohnstone


Your test should subscribe to the event OnInvalidEntry with a dummy method, call IsValidEntry and check the result.

like image 1
EkoostikMartin Avatar answered Oct 18 '22 00:10

EkoostikMartin