Pex it: Just ask the Mocks!
Pex it: Just ask the Mocks!
Submitted by Jonathan de Halleux on Sat, 02/06/2007 - 08:00.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.
Choose and replay
Pex Mocks have 2 modes of execution that are quite different:
- when Pex is looking for new input, they act as a source of parameter choices, (this will be clearer in the example below).
- when executing the generated unit tests, they act as stubs.
Micro example
Let us illustrate this by mocking a simple interface that returns the name of an instance:
interface INamed
{
string GetName();
}
Writing the mock
The mock for this interface looks as follows:
[PexMock] // specifies to pex that this type is a mock
public 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>();
Let's try it!
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");
}
Generated tests
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 2
MockTest.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");
Adding behavior
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 mock
public class MNamed : INamed
{
private string name;
public string GetName()
{
if(this.name == null)
this.name = PexOracle.MethodCall(this)
.ChooseNotNull<string>("name");
return this.name;
}
}
Wrapping Up
In this post, we've given a brief glimpse at the pex oracles. These are a special breed of mocks specific to Pex.
This posting is provided "AS IS" with no warranties, and confers no rights.
