How to propagate an event from a low level class to a top level one:

public class TopLevel{
    public bool Bubbled { get; private set; }
    private MiddleLevel observable;
    public TopLevel(MiddleLevel observable){
        this.observable = observable;
        observable.Triggered += (s, e) => {
            Bubbled = true;
        };
    }
}
public class MiddleLevel{
    public event EventHandler Triggered;
    private BottomLevel observable;
    public MiddleLevel(BottomLevel observable){
        this.observable = observable;
        //One may be tempted to bubble like this:
        //observable.Triggered += Triggered; 
        //However, Triggered is null unless there is already
        //a subscriber. This is a better approach:
        
        observable.Triggered += (s, e) => {
            Triggered(s, e);
        };
    }
}
public class BottomLevel{
    public event EventHandler Triggered;

    public void DoSomething(){
        Triggered(this, EventArgs.Empty);
    }
}

[TestFixture]
public class TestingEventBubbling {
    [Test]
    public void Bubbling(){
        var bottom = new BottomLevel();
        var middle = new MiddleLevel(bottom);
        var top = new TopLevel(middle);

        bottom.DoSomething();

        top.Bubbled.Should().BeTrue();
    }
}

Events can only be raised from within the declaring type. Unfortunately they can’t be be passed in as arguments to methods. Only += and -= operators are allowed out of the declaring type. One way to stub out the event could be through inheritance:

public class BottomLevel{
    public virtual event EventHandler Triggered;

    public void DoSomething(){
        Triggered(this, EventArgs.Empty);
    }
}

public class StubbedBottomLevel : BottomLevel {
    public override event EventHandler Triggered;

    public void RaiseEvent(){
        Triggered(this, EventArgs.Empty);
    }
}

[TestFixture]
public class TestingEventBubbling {
    [Test]
    public void BubblingWithStub(){
        var bottom = new StubbedBottomLevel();
        var middle = new MiddleLevel(bottom);
        var top = new TopLevel(middle);
        
        bottom.RaiseEvent();
        //bottom.DoSomething(); will not throw the event!

        top.Bubbled.Should().BeTrue();
    }

But declaring the event as virtual and then overriding it, is very tricky: replacing the call to RaiseEvent to DoSomething, makes the test fail! Looks like events where not designed to be overridden. A better approach:

public class BottomLevel{
    public event EventHandler Triggered;

    public virtual void DoSomething(){
        //SomeLogic would go here...
        Raise(EventArgs.Empty);
    }
    protected virtual void Raise(EventArgs args){
        Triggered(this, args);
    }
}

public class StubbedBottomLevel : BottomLevel {
    public override void DoSomething(){
        Raise(EventArgs.Empty);
    }
}