This post gives a little introduction on the integrated mock support in Pex (it should also make clear why we couldn't reuse existing (and excellent) mock framework).
As other frameworks, Pex support mocking interfaces or virtual methods in non sealed classes.
Pex Mocks have 2 modes of execution that are quite different:
Let us illustrate this by mocking a simple interface that returns the name of an instance:
interface INamed{ string GetName();}
The mock for this interface looks as follows:
[PexMock] // specifies to pex that this type is a mockpublic class MNamed : INamed{ public string GetName() { IPexMethodCallOracle call = PexOracle.MethodCall(this); // let's ask Pex for a name return call.ChooseResult<string>(); }}
[PexMock] // specifies to pex that this type is a mockpublic class MNamed : INamed{ public string GetName() { IPexMethodCallOracle call = PexOracle.MethodCall(this);
// let's ask Pex for a name return call.ChooseResult<string>(); }}
So what did we do really: The mock implementation of GetName fetches a value oracle, an interface from which new values can be queried:
IPexMethodCallOracle call = PexOracle.MethodCall(this);
Then, this oracle can be used to choose new values. It's like adding parameters to the test, but not through the test signature. In this case, we ask Pex to get a new result value. Pex will choose that value as if it was a parameter of the test:
return call.ChooseResult<string>();
The following test takes a INamed instance (which we assumed to be non-null) and displays a message on the console if the name is equal to'Marc':
[PexTest]public void GetName(INamed name){ if (name.GetName() == "Marc") Console.WriteLine("hello marc");}
Pex generates 2 unit tests that fully covers the code of method: Pex tracked the data returned by ChooseResult and computed that this value had to be equal to "Marc" to hit a different path in the execution.
The first test has GetName return a default value. The second generated test is more interresting as it shows how Pex sets up the behavior of the replay stub:
// test 2MockTest.MNamed mn0 = new MockTest.MNamed();IPexOracleRecorder oracle = PexOracle.NewTest();oracle.OnCall(0, typeof(MockTest.MNamed), "GetName") .Returns("Marc");this.GetName(mn0);
The oracle instance is used to set up the result value on the first call to GetName:
// on the call '0' to the method GetName of MockType.MNamed,oracle.OnCall(0, typeof(MockTest.MNamed), "GetName") // return "Marc" .Returns("Marc");
Behavior can simply be added to mock as code since Pex will explore the mock code as well. For example, let's refine the MNamed implementation to always return the same name:
[PexMock] // specifies to pex that this type is a mockpublic class MNamed : INamed{ private string name; public string GetName() { if(this.name == null) this.name = PexOracle.MethodCall(this) .ChooseNotNull<string>("name"); return this.name; }}
In this post, we've given a brief glimpse at the pex oracles. These are a special breed of mocks specific to Pex.
Page rendered at Friday, August 08, 2008 7:57:11 AM UTC
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.