dimanche 17 février 2019

NSubstitute raising an event on a mocked subclass

What I have is a wrapper around a class from an external library (this case it's WebSocketSharp), the wrapper class reacts on certain events like when a connection was established etc.

To test this wrapper class I've mocked the WebSocket class and did Raise.EventWith on that class, and I'm expecting the Wrapper class to do w/e handling it should.

Code is something like this:

public class WebsocketClient {
    public event EventHandler Connected;

    public WebSocket Connection { get;set; }

    public void ConnectAsync() {
        Connection.OnOpen += Connection_OnOpen;
        Connection.ConnectAsync();
    }

    private void Connection_OnOpen(object sender, System.EventArgs e) {
        Connected?.Invoke(this, new EventArgs());
    }
}

The test that I wanted to write is:

public void ConnectedTest() {
    var objClient = new WebsocketClient();
    var raised = false;
    objClient.Connected += delegate (object sender, EventArgs e) {
        raised = true;
    }; 
    var objWebSocket = Substitute.For<WebSocketSharp.WebSocket>("wss://localhost:443");
    objClient.Connection = objWebSocket;
    objClient.ConnectAsync();
    objClient.Connection.OnOpen += Raise.EventWith(new object(), new EventArgs());

    objWebSocket.Received().OnOpen += Arg.Any<EventHandler>();
    Assert.IsTrue(raised);
}

Obviously things were simplified a bit as there are a few more things to check, this is just to get the idea across.

Here in the test I want to verify 2 things, that when ConnectAsync is called an eventhandler is added to the OnOpen event and when the OnOpen is triggered I'm getting an event back from the class I'm testing.

What I know the response will be is that this is 'bad design', but that doesn't really help me much, how would you solve this?, I need to wrap the WebSocket class, this is not mine so have no say over it.

The only thing in this scenario I can think of is extending WebSocket class instead of making a wrapper, but I'm sure there are other tests I need to make where extending isn't an option and writing a wrapper like this will be needed, example when using a filesystemwatcher or a timer where the event handler is private and still want to test what happens when the even is fired.

To demonstrate, how would this for example be tested? (didn't run anything, it's just to show the idea)

public class FilesDeleted {
    private FileSystemWatcher _objWatcher;
    private List<string> _lstPaths;

    public event EventHandler ItemsDeleted;

    public FilesDeleted(string pPath) {
        _lstPaths = new List<string>();
        _objWatcher = new FileSystemWatcher(pPath);
    }

    public void Start() {
        _objWatcher.Deleted += _objWatcher_Deleted;
        _objWatcher.EnableRaisingEvents = true;
    }

    private void _objWatcher_Deleted(object sender, FileSystemEventArgs e) {
        _lstPaths.Add(e.FullPath); 
        if(_lstPaths.Count > 10) {
            ItemsDeleted?.Invoke(this, new EventArgs());
        }
    }
}

In the test you'd want to verify that after 10 file deleted events from the filesystemwatcher you get the "ItemsDeleted" event from this class.

Think the FileSystemWatcher example shows with what I'm struggling the most

Aucun commentaire:

Enregistrer un commentaire