Login using System.Noticeably.Different.WebSite;
March 16, 2010 NoticeablyDifferent
Search for
 
Testing The Observer Pattern 
    #region Code
public interface IObserver {
    void Update(Subject subject);
}
 
public abstract class Subject {
    protected IList<IObserver> observers = new List<IObserver>();
 
    public void Attach(IObserver observer) {
        observers.Add(observer);
    }
 
    public void Detach(IObserver observer) {
        observers.Remove(observer);
    }
 
    public void Notify() {
        foreach (IObserver observer in observers) {
            observer.Update(this);
        }
    }
}
    #endregion
 
    #region Unit Tests
public class MockSubject : Subject {
    public IList<IObserver> ObserverCollection {
        get {
            return base.observers;
        }
    }
}
 
public class MockObserver : IObserver {
    private bool updateCalled = false;
 
    public void Update(Subject subject) {
        updateCalled = true;
    }
 
    public bool UpdateCalled {
        get {
            return updateCalled;
        }
    }
}
 
[TestFixture]
public class SubjectTests {
 
    private Subject subject;
    private IObserver observer;
 
    public SubjectTests() { }
 
    [SetUp]
    public void SetUp() {
        subject = new MockSubject();
        observer = new MockObserver();
    }
 
    [Test]
    public void SubjectAttachesObserver() {
        AttachAndAssertMockObserverAttached();
    }
 
    private void AttachAndAssertMockObserverAttached() {
        subject.Attach(observer);
        Assert.AreEqual(1, ((MockSubject)subject).ObserverCollection.Count,
            "Subject attached");
        Assert.AreSame(observer, ((MockSubject)subject).ObserverCollection[0],
            "Observer is MockObserver");
    }
 
    [Test]
    public void SubjectDetachesObserver() {
        AttachAndAssertMockObserverAttached();
        subject.Detach(observer);
        Assert.AreEqual(0, ((MockSubject)subject).ObserverCollection.Count,
            "Subject detached");
    }
 
    [Test]
    public void SubjectNotifiesObserver() {
        Assert.IsFalse(((MockObserver)observer).UpdateCalled, "Update not called");
        AttachAndAssertMockObserverAttached();
        Assert.IsFalse(((MockObserver)observer).UpdateCalled, "Update still not called");
        subject.Notify();
        Assert.IsTrue(((MockObserver)observer).UpdateCalled, "Update called");
    }
}
    #endregion
 
 
The event/delegate subscription model in .NET performs essentially the same function as the Observer pattern and is equally testable without defining a specific handler in the subject. In this sample I test the subject using the event/delegate model and the anonymous delegate feature of .NET 2.0.
 
    #region Code
public class Subject {
    public event System.EventHandler Notify;
 
    public void NotifyObservers() {
        if (Notify != null) {
            Notify(this, new System.EventArgs());
        }
    }
}
    #endregion
 
    #region Unit Tests
[TestFixture]
public class SubjectTests {
    private bool updateCalled;
    private Subject subject;
 
    public SubjectTests() { }
 
    [SetUp]
    public void SetUp() {
        updateCalled = false;
        subject = new Subject();
        subject.Notify += delegate(object sender, System.EventArgs args) {
            updateCalled = true;
        };
    }
 
    [Test]
    public void SubjectNotifiesObserver() {
        Assert.IsFalse(updateCalled, "Update not called");
        subject.NotifyObservers();
        Assert.IsTrue(updateCalled, "Update called");
    }
}
    #endregion