Alex Rivera | Logout

Unit testing with Mocks when SUT is leveraging Task Parallel Libaray

Asked 2010-04-29T10:53:15.007
9

I am trying to unit test / verify that a method is being called on a dependency, by the system under test (SUT).

  • The depenedency is IFoo.
  • The dependent class is IBar.
  • IBar is implemented as Bar.
  • Bar will call Start() on IFoo in a new (System.Threading.Tasks.)Task, when Start() is called on Bar instance.

Unit Test (Moq):

    [Test]
    public void StartBar_ShouldCallStartOnAllFoo_WhenFoosExist()
    {
        //ARRANGE

        //Create a foo, and setup expectation
        var mockFoo0 = new Mock<IFoo>();
        mockFoo0.Setup(foo => foo.Start());

        var mockFoo1 = new Mock<IFoo>();
        mockFoo1.Setup(foo => foo.Start());


        //Add mockobjects to a collection
        var foos = new List<IFoo>
                       {
                           mockFoo0.Object,
                           mockFoo1.Object
                       };

        IBar sutBar = new Bar(foos);

        //ACT
        sutBar.Start(); //Should call mockFoo.Start()

        //ASSERT
        mockFoo0.VerifyAll();
        mockFoo1.VerifyAll();
    }

Implementation of IBar as Bar:

    class Bar : IBar
    {
        private IEnumerable<IFoo> Foos { get; set; }

        public Bar(IEnumerable<IFoo> foos)
        {
            Foos = foos;
        }

        public void Start()
        {
            foreach(var foo in Foos)
            {
                Task.Factory.StartNew(
                    () =>
                        {
                            foo.Start();
                        });
            }
        }
    }

Moq Exception:

*Moq.MockVerificationException : The following setups were not matched:
IFoo foo => foo.Start() (StartBar_ShouldCallStartOnAllFoo_WhenFoosExist() in
FooBarTests.cs: line 19)*
Edit
Report

2 Answers

7

@dpurrington & @StevenH: If we start putting this kind of stuff in our code

sut.Start();
Thread.Sleep(TimeSpan.FromSeconds(1)); 

and we have thousands of "unit" tests then our tests start running into the minutes instead of seconds. If you had for example 1000 unit tests, it will be hard to have your tests to run in under 5 seconds if someone has gone and littered the test code base with Thread.Sleep.

I suggest that this is bad practice, unless we are explicitly doing Integration testing.

My suggestion would be to use the System.Concurrency.IScheduler interface from System.CoreEx.dll and inject the TaskPoolScheduler implementation.

This is my suggestion for how this should be implemented

using System.Collections.Generic;
using System.Concurrency;
using Moq;
using NUnit.Framework;

namespace StackOverflowScratchPad
{
    public interface IBar
    {
        void Start(IEnumerable<IFoo> foos);
    }

    public interface IFoo
    {
        void Start();
    }

    public class Bar : IBar
    {
        private readonly IScheduler _scheduler;

        public Bar(IScheduler scheduler)
        {
            _scheduler = scheduler;
        }

        public void Start(IEnumerable<IFoo> foos)
        {
            foreach (var foo in foos)
            {
                var foo1 = foo;  //Save to local copy, as to not access modified closure.
                _scheduler.Schedule(foo1.Start);
            }
        }
    }

    [TestFixture]
    public class MyTestClass
    {
        [Test]
        public void StartBar_ShouldCallStartOnAllFoo_WhenFoosExist()
        {
            //ARRANGE
            TestScheduler scheduler = new TestScheduler();
            IBar sutBar = new Bar(scheduler);

            //Create a foo, and setup expectation
            var mockFoo0 = new Mock<IFoo>();
            mockFoo0.Setup(foo => foo.Start());

            var mockFoo1 = new Mock<IFoo&
answered 2010-12-01T10:56:33.857
0

Your tests uses too much implementation detail, IEnumerable<IFoo> types. Whenever I have to start testing with IEnumerable it always creates some friction.

answered 2010-04-29T13:55:21.173

Your Answer