Explorables
Pex can reliably create values of all primitive types, arrays, and types with
visible default constructors if all of their fields are
visible. However, if an object must be created by calling a constructor or factory method that takes parameters, then
Pex needs more information. (This way, Pex can even
use types that are not
visible.) In particular, Pex needs to know which constructor or factory method it should explore.
In general, a constructor might involve some control flow (parameter checking) or invoking different methods. Therefore, Pex needs to explore the constructor in addition to the actual test code. Types with constructors that involve control flow are called
explorable because they need to be 'explored' in order to obtain interesting objects.
A classic example
Let's consider the following
Account class; it has a
money field which is not
visible from the public context. In fact, the value of
money is computed in the constructor of
Account:
public class Account {
private float money;
public Account(Bank bank, string name) {
// control-flow in the constructor
if (bank == null) throw new ArgumentNullException();
if (name == null) throw new ArgumentNullException();
// calls to other method
this.money = bank.GetMoney(name);
}
public float Money {
get { return this.money; }
}
}
We want to write a test that states that for any valid instance of
Account, the value of money should be positive. With
Pex, we can pass an
Account instance as a test parameter:
// test that needs an instance of Account
[PexMethod]
public void NeedsAccount(Account a) {
Assert.IsTrue(a.Money >= 0);
}
To explore this test, Pex will need to create valid instances of
Account. Since the state of
Account cannot be set entirely from public fields, Pex needs to pick a constructor or a static method (or a sequence of constructor, methods, properties, etc...) to 'set' the state of
Account to the value it has computed.
While Pex makes a smart guesses, you can configure yourself how Pex should create an instance of a type. (And configuring how to create a type yourself is the only way to suppress Pex' notifications about its guesses.)
// binding
[assembly: PexExplorableFromConstructor(typeof(Account), typeof(Bank), typeof(string))]
// load any factories from this assembly
[assembly: PexExplorableFromFactories]
...
// this type contains factories
[PexClassFactory]
public static class AccountFactory {
// this is a factory that creates 'Account' instances
[PexMethodFactory(typeof(Account))]
public static Account Create(Bank bank, string name) {
return new Account(bank, name);
}
}
Attributes
Pex comes with the following set of derived attributes:
Restrictions
- Pex will not explore all code paths of the constructor. In particular, it will suppress paths that throw an exception in the constructor. Therefore, separate parameterized tests should be written to exercise the constructor.
- If another object is required by the constructor, Pex will first try to create that object, whose constructor might need another object, and so on.
See also
Creatables