This is a migrated thread and some comments may be shown as answers.

Test not working well when we try to parallel it

1 Answer 143 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Javier
Top achievements
Rank 2
Javier asked on 23 Oct 2013, 03:47 PM
Hi,

I have a test that have a Parallel.For in it. If I remove the Parallel.For, works fine, but if I try to do it in a parallel way I get an exception like if the Mocks where not applied.

Exception:

Test method Tgw.Wcs.StorageAndInventory.U.Test.Core.LoadCarrierManagerTest.UpdateLoadCarrierLocksLoadCarrier threw exception: 
System.AggregateException: One or more errors occurred. ---> System.InvalidOperationException: No connection string named 'StorageAndInventoryContext' could be found in the application config file.

Is like after the first thread is launched the rest of them cannot get the Mocked object...

[TestInitialize]
       public void Initialize()
       {
           loadCarrierManager = new LoadCarrierManager();
           loadCarrier = new LoadCarrier { Name = "LC1" };
           //Creates a number of elements
           loadCarriers = CreateLoadCarriers(numberOfLoadcarriers);
            
           //First Mock the SIS Context
           storageAndInventoryContext = Mock.Create<StorageAndInventoryContext>(() => new StorageAndInventoryContext("Non"),
                                                       Behavior.CallOriginal);
           //Mock the GetContext so it will return out Mocked element
           Mock.Arrange(() => StorageAndInventoryContextProvider.GetContext())
               .Returns(storageAndInventoryContext);
 
           Mock.Arrange(() => storageAndInventoryContext.InventoryAccountSet.OfType<LoadCarrier>())
              .ReturnsCollection(loadCarriers);
       }
 
 
 
       [TestMethod]
       [TestCategory(TgwTestingCategories.ServiceTest)]
       [WorkItem(11126)]
       public void UpdateLoadCarrierLocksLoadCarrier()
       {
 
          Parallel.For((long)0, 3, i =>
               {
                   using (var scope = new UnitOfWorkScope())
                   {
                       for (int lcCount = numberOfLoadcarriers-1; lcCount > 0; lcCount--)
                       {
                           string lcName = string.Format("LC{0}", lcCount);
 
                           LoadCarrierChangeSet loadCarrierChangeSet = new LoadCarrierChangeSet();
 
                           var lcToUpdate = loadCarrierManager.GetLoadCarrierData(lcName);
 
                           loadCarrierChangeSet.Locks = new LockChangeSet("fooReason", "fooDescription");
 
                           loadCarrierManager.UpdateLoadCarrier(lcToUpdate.Name, loadCarrierChangeSet);
                       }
 
                       scope.Complete();
                   }
             });
 
           using (new UnitOfWorkScope())
           {
 
               var badEntityLocksDescriptions =
                   storageAndInventoryContext.InventoryAccountSet.OfType<LoadCarrier>()
                                             .Select(x => x.EntityLocks.Where(y=>y.Description == "fooReason"));
 
 
               Assert.AreEqual(100, badEntityLocksDescriptions.Count());
           }
       }



1 Answer, 1 is accepted

Sort by
0
Kaloyan
Telerik team
answered on 25 Oct 2013, 12:08 PM
Hi Javier,

Thank you for contacting us.

Your conclusion about not applying static mocks in parallel execution is correct.
For example, take the following:

We have a static class, like this:
public static class Foo
{
    public static string Prop { get; set; }
}

Now, we can write the following test:
[TestMethod]
public void NormalExecution()
{
    Mock.Arrange(() => Foo.Prop).Returns("test");
 
    for (int i = 0; i < 3; i++)
    {
        var actual = Foo.Prop;
 
        Assert.AreEqual("test", actual);
    }
}
It will work as expected. However, if we use Parallel.For, the test will fail:
[TestMethod]
public void ExecutionInParallel()
{
    Mock.Arrange(() => Foo.Prop).Returns("test");
 
    Parallel.For(0, 3, i =>
    {
        var actual = Foo.Prop;
 
        Assert.AreEqual("test", actual);
    });
}
This is because, as noted above, the static arrangement is not applied for all threads.

One way to make the above test green (to make it pass) is to add the mock creations and their arrangements inside the parallel for, like this:
[TestMethod]
public void ExecutionInParallel()
{
    Parallel.For(0, 3, i =>
    {
        Mock.Arrange(() => Foo.Prop).Returns("test");
 
        var actual = Foo.Prop;
 
        Assert.AreEqual("test", actual);
    });
}
This, however is not a good practice as it leads to more complicated and tiresome tests.

Another way to make this work, is to use a singleton. Below is the a singleton of the previous Foo class:
public class SingletonFoo
{
    private static SingletonFoo myInstance = new SingletonFoo();
    private static object syncRoot = new Object();
 
    private SingletonFoo() { }
 
    public static SingletonFoo GetInstance
    {
        get
        {
            if (myInstance == null)
            {
                lock (syncRoot)
                {
                    if (myInstance == null)
                        myInstance = new SingletonFoo();
                }
            }
            return myInstance;
        }
    }
 
    public string Prop { get; set; }
}

Now, the parallel unit test works:
[TestMethod]
public void ExecutionInParallel()
{
    var myFoo = SingletonFoo.GetInstance;
    Mock.Arrange(() => myFoo.Prop).Returns("test");
 
    Parallel.For(0, 3, i =>
    {
        var actual = myFoo.Prop;
 
        Assert.AreEqual("test", actual);
    });
}

To summarize:
  • Firstly, I do not recommend parallel test execution. It may give performance, but a lot of concurrency issues can occur.
  • If you decide to stick to the parallel test execution, I suggest using singleton classes instead of statics. By far, this is the only reliable way to isolate the different threads.
  • If singleton is not an option for you (e.g.: you may need to refactor a lot of code), you can try adding the mock creations and their arrangements in parallel, as well.

I hope this gives the required answers. Let me know if I can assist you further.


Regards,
Kaloyan
Telerik
Share what you think about JustTrace & JustMock with us, so we can become even better! You can use the built-in feedback tool inside JustTrace, our forums, or our JustTrace or JustMock portals.
Tags
General Discussions
Asked by
Javier
Top achievements
Rank 2
Answers by
Kaloyan
Telerik team
Share this question
or