Moles/Fakes: How do I implement a test setup?
Asked Answered
E

2

5

I have been recently working with Moles and I am now switching to Fakes. In my old test project I had a test setup, which looked like this:

[TestInitialize]
public void Setup()
{
    //...
}

In there I did some necessary setup just like setting up some of my moles objects.

A test method in moles looked somehow like that (with also the [HostType("Moles")] specifying that it uses the moles objects.

[TestMethod]
[HostType("Moles")]
public void MolesTestMethod()
{
    //...
}

Now, in Fakes they do not use the HostType Attribute anymore. Instead they use a ShimsContext in which you can use your "mocked" classes. It looks somehow like this:

[TestMethod]
public void FakesTestMethod()
{
    using (ShimsContext.Create())
    {
        //...
    }
}

If you do not use this context you might end up with an error message. It basically says that there was an ShimInvalidOperationException in the FakesTestMethod and you have to use the ShimsContext.Create() in the way described below)

-- C#:
using Microsoft.QualityTools.Testing.Fakes;

using(ShimsContext.Create())
{
    // your test code using Shims here
}

-- VisualBasic.NET
Imports Microsoft.QualityTools.Testing.Fakes

Using ShimsContext.Create
    ' your test code using Shims here
End Using  

So I tried to put my setup calls into that context and ended up with something like this:

[TestInitialize]
public void Setup()
{
    using(ShimsContext.Create())
    {
        //...
    }
}

Now, if I use this context in my Setup method, all the setup that is done there, will run out of context afterwards and will not be valid anymore when the unit tests are actually about to run, which is not really what I want from a test setup method.

I fixed this issue by putting the using inside the test method itself and just call a private setup method right inside this context and before the test code. This setup method now does all the handling, which before the [TestInitialize] setup method did. The code looks somehow like this:

[TestMethod]
public void PerformActionFromConfigActionStateActionIdIsSet()
{
    using (ShimsContext.Create())
    {
        Setup();

        //...
    }
}

My problem with this issue is now, that this solution completely "kills" the idea of the [TestInitialize] setup method. I have to duplicate this code into EACH test method and the most important part: the objects created in this Setup() method will be created and destroyed for EACH test, which is not ideal at all!

Is there any other way to setup test data in Fakes? Any help is appreciated!

Easting answered 18/8, 2012 at 10:12 Comment(0)
W
12

Using:

Defines a scope, outside of which an object or objects will be disposed.

You create an IDisposable instance by calling ShimsContext.Create() and wrap it by an using block. After initializing your Fakes classes and leaving the using scope your configuration gets disposed.

I would recommend creating the IDisposable instance and calling Dispose at the end of the tests manually.

If you want to avoid Context creation for each test, I would also recommend using ClassInitialize and ClassCleanup instead of TestInitialize and TestCleanup since it should be sufficient initializing Shims once for alle tests. This is only possible if there are no additional dependencies (see Oleg Sych answer).

[TestClass]
public class TestClass1
{
    protected static IDisposable Context { get; set; }

    [ClassInitialize]
    public static void ClassInitialize(TestContext testContext)
    {
        // Create ShimsContext
        Context = ShimsContext.Create();

        // TODO: Additional setup
    }

    [ClassCleanup]
    public static void ClassCleanup()
    {
        Context.Dispose();
        Context = null;
    }

    [TestMethod]
    public void TestMethod1()
    {
        // Fakes should be initialized correctly here
    }

    [TestMethod]
    public void TestMethod2()
    {
        // Fakes should be initialized correctly here
    }
}

Hope that helps.

Walford answered 30/8, 2012 at 14:23 Comment(0)
V
4

Using ClassInitialize/ClassCleanup to initialize shims is not a good idea. This will make detours configured for one test method remain active for all other test methods in the class. In other words, detours and any additional state captured by lambdas becomes shared between all test methods. This can also destabilize the test harness if you detour methods it happens to use.

Instead, use TestInitialize/TestCleanup to create/dispose the ShimsContext for each test method separately.

Vermeil answered 13/10, 2012 at 0:18 Comment(3)
Since this is a comment to my answer and not a answer to the original question, feel free to use the comment function. If there are no additional dependencies inside mocked functions, it's better to use ClassInitialize (since it is faster) - you're right, if there are dependencies you must use TestInitialize instead. Because of the author's sentence "the objects created in this Setup() method will be created and destroyed for EACH test, which is not ideal at all!" i assumed the author DID NOT want to create the Context for each test, why i offered to use ClassInitialize.Walford
Again, creating a ShimsContext and configuring detours in ClassInitialize is a bad idea because it can destabilize the test harness. This means seemingly random test failures that have nothing to do with the tests themselves and will be difficult to understand. Don't do it.Vermeil
Ok, i believe you. I know that there where issues in moles (bit.ly/VztXBR), but i wasn't able to reproduce that in fakes. Surprisingly i used this setup quite often and didn't encounter any unexpected test results. Are there any additional resources available explaining this issue?Walford

© 2022 - 2024 — McMap. All rights reserved.