Reputation: 2530
How do you test multiple values for same Attribute ?
class Test {
private $_optionalValue = null;
function setValue(String $optionalValue)
{
$this->_optionalValue = $optionalValue;
}
}
So here, "$_optionalValue" could be NULL or User defined value, but when I check with phpunit like this :
$optionalValue = PHPUnit_Util_Class::getObjectAttribute($my_object, '_optionalValue');
$this->assertThat(
$optionalValue,
$this->logicalXor(
$this->assertNull($optionalValue),
$this->logicalAnd(
$this->assertAttributeInternalType('string', '_optionalValue', $optionalValue),
$this->assertRegExp('/[0-9]{2}:[0-9]{2}:[0-9]{2}/', (string) $optionalValue)
)
)
);
The Regexp assertion fails because $optionalValue isn't a String (it's null by default)
Upvotes: 1
Views: 4217
Reputation: 36522
You are making assertions inside your call to assertThat
, but you need to build and pass in constraints instead. All of the methods that start with assert
evaluate the value immediately and throw an exception on mismatch. Each assertion has a corresponding constraint class, some with a factory method.
$optionalValue = PHPUnit_Util_Class::getObjectAttribute($my_object, '_optionalValue');
$this->assertThat(
$optionalValue,
$this->logicalXor(
$this->isNull(),
$this->logicalAnd(
new PHPUnit_Framework_Constraint_IsType('string'),
new PHPUnit_Framework_Constraint_PCREMatch('/[0-9]{2}:[0-9]{2}:[0-9]{2}/')
)
)
);
BTW, I do agree that a) you are better off not testing internal state like this and b) you should design your tests so that you know which value to expect. Each test should put the system into a single expected state.. Even code that uses random numbers should substitute a fixed sequence using a stub. Any test that allows multiple possibilities is suspect.
Upvotes: 2
Reputation: 197554
You're testing a private property of an object which should be generally avoided because that's an internal of the unit and you should not care about that.
If your unit needs to validate values of that class, it's likely that generally a value of some kind should be validated.
You could therefore encapsulate the logic of the validation into a unit of it's own, e.g. a validator:
class FooValueValidator implements Validator {
/**
* @var string
*/
private $value;
/**
* @var string
*/
private $regex = '/[0-9]{2}:[0-9]{2}:[0-9]{2}/';
public function __construct($value) {
$this->value = $value;
}
/**
* @return bool
*/
public function isValid() {
if (is_null($this->value)) {
return TRUE;
}
if (!is_string($this->value)) {
return FALSE;
}
$result = preg_match($this->pattern, $this->value);
if (FALSE === $result) {
throw new Exception(sprintf('Regular expression failed.'));
}
return (bool) $result;
}
}
You can then write unit-tests for the validator. You then know that your validator works and you can use it everywhere you like.
class Test {
private $_optionalValue = null;
/**
* @var Validator
*/
private $_validator;
public function __construct(Validator $validator) {
$this->_validator = $validator;
}
function setValue(String $optionalValue)
{
if (!$this->validator->isValid($optionalValue)) {
throw new InvalidArgumentException(sprintf('Invalid value "%s".', $optionalValue));
}
$this->_optionalValue = $optionalValue;
}
}
Upvotes: 1