Preview and Questions on the next Isolator API

Here is a preview of some use cases from the new Typemock Isolator API coming out sometime in August. I have some questions for you which I’d love for you to answer in the comments.

First, a little background on the new API that should come out sometime in august.

  • The new Isolator API goes in the direction of AAA – Arrange,Act,Assert and backs away from the more traditional Record-Replay model.
  • No mocks or stubs. Everything is defaulted to a fake object, which can either be used as a stub or as a mock object later on. If at the end of the test you call “Verify” on that object, that it is logically a mock. if you tell that object to return specific values or throw exceptions during the test, it is a stub. but the API does not mention these words for clarity.
  • No strings (Except for non public members)
  • There is a single point of entry to the API, currently called “MyTest” which is used to create all the types of fakes.
  • There are several categories of faking which can be done, and they are represented in the MyTest as properties: “Make”, “Static”, “Swap”,”NonPublic”.
    • Make – Creates instances of fake objects to be used later on in the test. can be interfaces or real classes.
    • Static – used for faking out static methods or constructors
    • Swap: used for replacing objects that will be created in the future with fake objects
    • NonPublic – used as the entry point for faking out anything that is not accessible via publicly accessible apis.

Let’s see some use cases:

 

Use case #1: fake

A simple fake that will be used as a stub(all stubs are non strict by default – you can call any method that is void without getting an exception):

  • Travis Illig

    Is the old API still going to be available? I’d hate to have to rewrite hundreds (of not thousands) of unit tests.

    I’m assuming “MyTest” is not the final name of the object that will be the entry point. It’d just be confusing if not.

    The top of the post references “Make” but all of the tests show “Fake.” Typo?

    System.Reflection uses “NonPublic” – I’d keep “NonPublic” to be consistent.

    I think it’d be OK to throw an exception if you specify a public method in a nonpublic fake setup. Again, from a consistency perspective, if you tell reflection to list out nonpublic members, you’re not going to magically get public ones in the list. I’d make sure the exception message is very clear, though, and include some sort of action – “You tried to call public method ‘Foo’ as a nonpublic. Use ‘MyTest.WhenCalled’ instead of ‘MyTest.NonPublic.WhenCalled’”

    That said, I liked Eli’s post where there was only “MyTest.WhenCalled” and no “MyTest.NonPublic.WhenCalled” – the addition of .NonPublic in there felt inconsistent, like I should have a corresponding MyTest.Public.WhenCalled… but then, does it really matter? (If you lose the NonPublic thing entirely, then you shouldn’t throw any exceptions if the person accesses a public method using a string name.)

    Eli’s post also loses the .Static for static method calls. I also like that. I can see there might be some interesting challenges when you get to handling nonpublic statics, but the idea holds – do you need to differentiate? (Eli’s post just shows the name of the nonpublic static method being called… but not the type it’s attached to.)

    Is there a way to mock calls to base class methods? In the current API we have CallBase. Is there an equivalent here?

    I also like Eli’s “Members.ReturnFakes” example. That HttpRequest thing is even the precise use case where I could see it being handy.

    Of course, it may be that the only thing I need to fake is something nested in the request, like a particular cookie coming in. Right now, I can use the recorder API with a chained method call and just have it “work.” Not sure how I’d do that here, but it looks like it might be a bit more cumbersome.

    I can see this would be very powerful in a DI scenario. Particularly with a framework like Autofac where you can register dependencies using lambdas. Create your fake right in the dependency registration for your tests.

    Interesting stuff. Going to have to think on this some more.

  • Eli Lopian

    Thanks for the input Travis.

    The old API will still be available.

    I kinda liked MyTest, what would you prefer?

    We will support Base Calls using OnBase

    MyTest.WhenCalled(() => fake.Log()).OnBase().WillReturn("555");

    We will still have chained methods.
    So you can do:

    // I don't care about HttpWebRequest
    var fakeWebRequest = MyTest.Fake.Instance<HttpWebRequest>(Members.ReturnFakes);
    //setup a specific scenario
    MyTest.WhenCalled(() => fakeWebRequest.GetResponse().Cookies["Travis"]).WillReturn("fakeCookie");

  • Travis Illig

    “MyTest” just feels like it’s something that you use when you’re posting an example code snippet and you’re talking about your unit test or test fixture. Sort of like “MyClass” or “Foo.” Maybe have a “Typemock” object or an “Isolator” object – Isolator.WhenCalled(…). That way I’d know that I’m acting on a fake/stub/mock thing and not on the test or test fixture.

    Love the chained method example. Also the OnBase(). Perfect.

  • Andrew Kutruff

    In general I liked the record model of TypeMock, however it did cause problems with stubbing. You'd have to set VerifyMode to pass if not called, and then have to set it back for your expectations. It would be great if you could do another block in the test setup for stubbing and then have an expectation block for each individual test. Otherwise much of the stubbing has to be repeated.

    In general I don't like having to always put: recorder.ExpectAndReturn(person.Name, "Bob").Always() or MyTest.Blah().Blah().Whenever()
    around all my code, which is why I liked the recorder. It would be very nice if stubbing were made first class and separate from expectations and be able to control the default repeat
    recorder.StubMode = StubMode.AlwaysReturn;
    recorder.Return(person.Name)

    Or, even better:

    For a recording stub block, make property assignments really mean that they will return what's assigned, kind of like a list initializer in C#:

    Person person = Recorder.CreateMock <Person>();

    using (StubRecorder stubber = Recorder.CreateStubRecorder)
    {
    person.Name = "Bob";
    person.Age = 3;
    person.CalculateSomething();
    stubber.Return(7);
    }

    The propeerty assignements in the above block specify the return values. For methods, the standard recorder.Return() would suffice.

    That way the syntax is nice, and way less noise of having to put MyTest.Expect.Always() around everything.

  • matt

    +1 on renaming "MyTest" to something like "Isolator". Also, the syntax here is not exactly pretty – how about this:

    Mock Creation:

    var mock = Isolator.Mock<ICommand>();

    Public Instance Method:

    Isolator.Expect(() => mock.Execute()).Returns(2);

    Static Public Method:

    Isolator.Expect(() => MyService.StaticMethod()).Returns(2);

    Non-Public Instance Method:

    Isolator.Expect(() => mock.NonPublicMethod(“MyPrivateMethod”)).Returns(2);

    Public Members:

    Isolator.Expect(() => mock.Name).Returns(“Bob”);

    Non-Public Members:

    Isolator.Expect(() => mock.NonPublicMember("_name")).Returns(“Bob”);

  • Aaron Jensen

    you went overboard w/ the MyTest.Something.RealMethod, get rid of the Something for most things.

    I don’t get #8, I assume you mean log.Log not real.Log. That said, why do I have to “Fake” it first? That’s a wasteful line.

    why do I have a separate API for stubbing different things? Just make one that can stub everything. takes string or delegate

    I don’t like WhenCalled/WillReturn, tries to hard to be english and fails at that. Just call it Stub/Returns/Throws

    MyTest is really bad, call it Isolator or something like everyone suggested

  • Paulo Morgado

    I also don’t like MyTest. I’m writing my test and having something called MyTest is just confusing. My first thought was also Isolator, but anything better than MyTest would be welcome.

    I share Travis’ concern about the old API going away. I wouldn’t mind if the new API would be a new system and we could use both in the same test run, even if only one could be used in each test.

    I’ve suggested in the past the use of expression trees in reflective mocks, but what I really like are the natural mocks.

    I make extensive use of MSTest helpers to access non public members and it works fine with natural mocks most of the times. I only use reflective mocks when, for some reason, I can’t use natural mocks.

    I would also like to be able to create derived classes and add attributes and implicit and explicit interface implementation:

    Isolator.FakeDerived.Instance[ILogger]().Implments[ISerializable](InterfaceImplementatio.Explicit).WithAttribute(new SerializableAttribute());

  • Tim Bassett

    What is the empty parens syntax being shown with in the new API methods?

    That does seem too intuitive to me.

    Also, I'd like to agree on the "MyTest" and say I like "Isolator".

    e.g. MyTest.Static.WhenCalled( () => Logger.Log )

  • Gil Zilberfeld

    Tim,

    The empty parentheses are Lambda notation for delegates.

    What do you mean by Too intuitive?

    Gil