2

I am using NUnit in combination with NSubstitute to test an application. The application uses the Caliburn.Micro MVVM framework. Consider the following example code that I would like to create a test for. It utilizes Caliburn Micro's event aggregator class to publish a message. I would like to verify that the event published in this code actually contains the integer list that I expect (as indicated by the comment in the test code below).

public class ExampleEvent
{
    List<int> SampleValues { get; set; }
}

public class ExampleClass
{
    private IEventAggregator _eventAggregator;

    public ExampleClass(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator
    }

    public void ExampleMethod()
    {
        var exampleArray = new List<int>{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        _eventAggregator.PublishOnUIThread(new ExampleEvent { SampleValues = exampleArray });
    }
}

A test for the code above might look like the following...

[TestFixture]
public class ExampleClassTests()
{
    private ExampleClass _uut;
    private IEventAggregator _eventAggregator;

    [SetUp]
    public void SetUp()
    {
        _eventAggregator = Substitute.For<IEventAggregator>();
        _uut = new ExampleClass(_eventAggregator);
    }

    [Test]
    public void ExampleMethod_ShouldRaiseEvent()
    {
        _uut.ExampleMethod();

        // I would like to add something like the line below but errors are thrown when it is executed...
        _eventAggregator.Received().PublishOnUIThread(Arg.Is<ExampleEvent>(x => x.SampleValues.Equals( new List<int>{ 1, 2, 3, 4, 5, 6, 7, 8, 9 } )))
    }
}

How would one properly accomplish this?

GnUfTw
  • 337
  • 1
  • 4
  • 19

3 Answers3

3

As NKosi points out, be careful with the extension method here.

In terms of asserting on the list used there are a few options. This answer uses SequenceEqual with Arg.Is to check the items are equal. Or you can use a custom extension. There is also an experimental API (subject to change) in NSubstitute that can let you define more involved argument matchers, including incorporating existing assertion frameworks.

Finally, I sometimes find it easiest to just store the argument used and assert using your favourite unit test/assertion framework. For example:

ExampleEvent eventPublished = null;
eventAggregator.Publish(Arg.Do<ExampleEvent>(x => eventPublished = x), Arg.Any<Action<System.Action>>());

//ACT
_uut.ExampleMethod();

//ASSERT
MyTestFramework.Assert.CollectionsEqual(
    new List<int>{ 1, 2, 3, 4, 5, 6, 7, 8, 9 },
    eventPublished.Samples);
David Tchepak
  • 9,826
  • 2
  • 56
  • 68
  • I like the idea of storing the argument used and then making assertions on it later, seems flexible. Also, thanks for reiterating what NKosi brought up about PublishOnUIThread being an extension method, as I now realize that extension methods are static and therefore can't be mocked, atleast trivially. – GnUfTw Apr 30 '18 at 17:22
1

PublishOnUIThread is an extension method that is called on IEventAggregator.Publish.

/// <summary>
/// Publishes a message on the UI thread.
/// </summary>
/// <param name="eventAggregator">The event aggregator.</param>
/// <param name = "message">The message instance.</param>
public static void PublishOnUIThread(this IEventAggregator eventAggregator, object message) {
    eventAggregator.Publish(message, Execute.OnUIThread);
}

You will thus need to mock IEventAggregator.Publish in order to verify your tests exercised as expected.

_eventAggregator.Received().Publish(Arg.Any<ExampleEvent>(), Arg.Any<Action<System.Action>>());
Nkosi
  • 235,767
  • 35
  • 427
  • 472
0

My issue stemmed from not realizing that arrays will not compare equal to each other even if they have the same elements. I was able to check that the event aggregator received a call to the PublishOnUIThread() method with the expected arguments as follows.

_eventAggregator.Received().PublishOnUIThread(Arg.Is<StageOneSamplesAvailableEvent>(message => message.Samples.SequenceEqual(new List<double> { 10, 10, 10, 0, 0, 0, 0, 0 })));
GnUfTw
  • 337
  • 1
  • 4
  • 19