chevron-thin-right chevron-thin-left brand cancel-circle search youtube-icon google-plus-icon linkedin-icon facebook-icon twitter-icon toolbox download check linkedin phone twitter-old google-plus facebook profile-male chat calendar profile-male
Welcome to Typemock Community! Here you can ask and receive answers from other community members. If you liked or disliked an answer or thread: react with an up- or downvote.
0 votes
I have a class that has different behavior on the second call to a method than the first, depending upon the interaction with dependent classes.

For the first call, I need to set up mocks of the dependencies. On the second call, I need to override the behavior. Some of the behavior is complex, using DoInstead to determine what the behavior of the mocked method should be. After the first call, I want to reset the behavior. I've found that I cannot seem to override behavior if I have already called DoInstead to establish behavior. A more-forceful reset via Isolate.CleanUp results in unexpected behavior -- mocked classes start calling into the real implementation, calls to Isolate.Verify complain that mock objects are not mock objects, etc.

Is there a way to do this?

    [TestClass]
    public class UnitTest1
    {
        public class Dependency
        {
            public int Method1() { return 0; }
        }
        public class Test
        {
            private Dependency _dep;
            public Test(Dependency dep)
            {
                _dep = dep;
            }
            public int TestMethod()
            {
                return _dep.Method1();
            }
        }
        [TestMethod]
        [Isolated]
        public void TestMethod1()
        {
            Dependency dep = Isolate.Fake.Instance<Dependency>();
            Test target = new Test(dep);
            Isolate.WhenCalled(() => dep.Method1()).DoInstead(context => 4);

            int actual = target.TestMethod();
            Assert.AreEqual(4, actual);

            Isolate.WhenCalled(() => dep.Method1()).DoInstead(context => 6);

            actual = target.TestMethod();
            Assert.AreEqual(6, actual);
        }


This test will fail as the second call to Method1 returns 4.
asked by kevinms99 (4.4k points)

3 Answers

0 votes
Hi Kevin,

It seems like you find a bug. We'll resolve it and get back to you.

As a workaround, use this test (note that the DoInstead calls are in the Arrange part before calling the method):
[TestMethod]
[Isolated]
public void TestMethod1()
{
    Dependency dep = Isolate.Fake.Instance<Dependency>();
    Test target = new Test(dep);
    Isolate.WhenCalled(() => dep.Method1()).DoInstead(context => 4);
    Isolate.WhenCalled(() => dep.Method1()).DoInstead(context => 6);

    int actual = target.TestMethod();
    Assert.AreEqual(4, actual);

    

    actual = target.TestMethod();
    Assert.AreEqual(6, actual);
}


Regardless of the bug, please note that as a good rule of thumb, it is better to separate the two cases into separate tests. If they run in the same test, and the first Assert fails, you won't know what happens in the second one.
answered by gilz (14.5k points)
0 votes
Thanks for the response.

I've wondered about my general approach here. My class contains state, and the routines that I'm testing manipulate that state. In some cases, the behavior changes based on the previous state. An extreme simplification of the issue is something like:

        public class Stateful
        {
            private int _state = 0;

            public int DoSomething(int newState)
            {
                if (_state == 0)
                {
                    _state = newState;
                    return 4;
                }

                return newState;
            }
        }


You either need to 1) manipulate the private _state variable or 2) call DoSomething more than once within a single test. I've generally tried to avoid manipulating private variables as I think that makes my tests fragile. However, calling DoSomething more than once results in the awkwardness that I've got in trying to set up expectations and validate behavior.

Any advice?

Perhaps the initial state should be encapsulated in another class that the unit test can manipulate? In production code, the argument would either not be used or would pass the true default state.
answered by kevinms99 (4.4k points)
0 votes
Hi Kevin,

Tough question, since I assume the code sample is oversimplified.
As a guideline, I'd rather test through the interface if possible. So in your case I would rather call DoSomething a few times to get to the testable state, then assert on the returned result. I prefer that to checking the internal state.

But like I said, it's a guideline. Your code may vary. If you can't test exposed state, then I may try checking internal state. I always say that tests are about managing risk, and you need to decide how much you're willing to invest in testing (price being a fragile test).

I hope that helps. Actually, I'm going to talk about this in my webinar tonight.

Hope that helps,
answered by gilz (14.5k points)
...