Data Validation Tests in our application are done with the DataValidationTestTrait.
This trait in most cases first checks the given fields validation with a positive case, where it should run throught without problem and then with a negative case where it should cause an expected validation error.
This usually leads to quite a few lines of code like this:
public function testValidationDefault(): void { $accounts = $this->Accounts; $field = 'uuid'; $this->testDataValidationScalar($accounts, $field); $this->testDataValidationRequired($accounts, $field); $this->testDataValidationNotEmpty($accounts, $field); $this->testDataValidationUuid($accounts, $field); $field = 'name'; $this->testDataValidationScalar($accounts, $field); $this->testDataValidationMaxLength($accounts, $field, 120); $this->testDataValidationRequired($accounts, $field); $this->testDataValidationNotEmpty($accounts, $field); $field = 'email'; $this->testDataValidationEmail($accounts, $field); $this->testDataValidationRequired($accounts, $field); $this->testDataValidationNotEmpty($accounts, $field); $field = 'company'; $this->testDataValidationScalar($accounts, $field); $this->testDataValidationMaxLength($accounts, $field, 120); $this->testDataValidationRequired($accounts, $field); $this->testDataValidationNotEmpty($accounts, $field); $field = 'currency'; $this->testDataValidationScalar($accounts, $field); $this->testDataValidationMaxLength($accounts, $field, 3); $this->testDataValidationRequired($accounts, $field); $this->testDataValidationNotEmpty($accounts, $field); }
A recent problem I had with the DataValidationTestTrait was the “testDataValidationIsUnique” method.
The problems with it were:
I currently have 2 possible solutions for this in mind
So it ended up being a much simpler mistake. We forgot to disable validation on BOTH the times it was saved. The existing logic actually allows there being multiple different errors since it searches for a specific one in the list of errors it gets. We corrected it now in the trait itsself and it works properly now. The issue that you have to give it all the required fields still exists tho since it is not a data validation error. So an implementation would now look like this:
$this->testDataValidationIsUnique( $subscriptions, 'uuid', '12345681-4ad6-43e7-842c-aeb392a0f862', [ 'account_id' => '1', 'resource' => Resource::MEM->value, 'amount' => 17179869184, 'price' => 150.50, 'status' => SubscriptionStatuses::ACTIVE->value, 'auto_renew' => 1, 'free_tier' => 0, 'start_time' => '2025-11-07 15:46:40', 'end_time' => '2026-11-07 15:46:40', 'period' => '365 days, 1:28:20.246798', ] );
And the trait now looks like this:
protected function testDataValidationIsUnique( Table $table, string $fieldName, mixed $fieldValue, array $additionalProperties = [], ?array $expected = null, ): void { $prevEntity = $table->newEmptyEntity(); $table->patchEntity( $prevEntity, array_merge($additionalProperties, [$fieldName => $fieldValue]), ['validate' => false], // <--- We noticed this before ); $table->saveOrFail($prevEntity); $entity = $table->newEmptyEntity(); $table->patchEntity( $entity, array_merge($additionalProperties, [$fieldName => $fieldValue]), ['validate' => false], // <--- BUT WE MISSED THIS ); $result = $table->checkRules($entity); static::assertFalse($result); $expected ??= ['_isUnique' => 'This value is already in use']; $this->assertDataValidationErrorsContain($fieldName, $entity->getError($fieldName), $expected); }