Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling fixture factories for a table breaks application code that uses that table downstream #187

Open
jamisonbryant opened this issue Oct 5, 2022 · 4 comments

Comments

@jamisonbryant
Copy link

jamisonbryant commented Oct 5, 2022

This was the bug at the bottom of a well that I spent many, many days trying to diagnose.

Description of the problem

It appears that if fixture factories touch a table and then downstream application code also does, entity creation does not work for that table in the application code.

How to reproduce

  • I have a test class that calls a method that makes (using Fixture Factories) 5-10 entities from the AccessTokensTable and returns them (they are not persisted).
  • These entities are then used in the test...and all these tests pass. Call this test class BadTest.
  • I have another test class (call it InnocentTest) which is an Integration test. Several tests within it POST to a controller action in my application code.
  • This controller action is supposed to create a new entity using the same table as was previously used by the fixture factories. (AccessTokensTable). This entity should be persisted, by the app.
  • Any and all tests that call that action fail because the entity that was supposed to be generated is not generated.

I was able to confirm that the problem occurs as a result of the call to AccessTokensFactory::make(), as refactoring my test data provider to manually construct the entities instead of calling the fixture factory results in all tests in the InnocentTest test class passing once again.

Expected behavior

Whatever magic the fixture factories do to the tables in order to create entities, that magic does not interfere with application code being able to interface with the tables and make entities downstream.

Let me know if all this is clear? I can try to put together an open-source sample app if needed.

@pabloelcolombiano
Copy link
Collaborator

@jamisonbryant thank for opening the issue. A sample app would be definitely appreciated, in order to reproduce the issue.

@jamisonbryant
Copy link
Author

jamisonbryant commented Jan 12, 2023

I haven't had time to put together a sample app, but I do think I have discovered something relevant. Adding these two lines:

// vendor/vierge-noire/cakephp-fixture-factories/src/Factory/EventCollector.php:91
$conn = $table->getConnection();
echo __CLASS__ . ' table connection = ' . PHP_EOL . $conn->configName() . PHP_EOL;

prints "foo" which is one of my database connections instead of "test_foo" as I would expect.

Is it my understanding that when the Cake test suite is running, the defaultConnectionName() of each table is supposed to be prepended by "test_" forming the complete connection name of "test_foo".

I'm not sure what an event collector does, but is there any chance it is not respecting the default cake convention for table names when running unit tests? Could that be the source of this problem?

@jamisonbryant
Copy link
Author

Here is more evidence that the BaseFactory's $eventCollector is flipping back and forth between connections:

CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (bottom check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = test_saas
CakephpFixtureFactories\Factory\EventCollector table connection (bottom check) = saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = saas
CakephpFixtureFactories\Factory\EventCollector table connection (top check) = saas

Top check = EventCollector::getTable() line 78, inside the if block
Bottom check = EventCollector::getTable() line 99, right before the function return

@jamisonbryant
Copy link
Author

jamisonbryant commented Mar 16, 2023

Mar 2023 update: I was able to properly resolve this issue, at long last!

I was right about application code trying to access the table registry at the same time as the unit tests, and using different connections to do so. E.g. my fixture factories would create and persist entities using the test connection, and my app would do the same except using the app connection. For my project, these are two different databases.

The solution is to alias the connections in the test bootstrapper:

// file: tests/bootstrap.php
$testDatasource = 'foo';
try {
	ConnectionManager::get($testDatasource);
	ConnectionManager::alias($testDatasource, 'default');
	ConnectionManager::alias($testDatasource, 'test');
	ConnectionManager::alias($testDatasource, 'other');

	//(new SchemaLoader())->loadSqlFiles(TESTS . 'schema.sql', $testConnection);
	(new Migrator())->run(['connection' => $testDatasource]);
} catch (MissingDatasourceConfigException $e) {
	trigger_error("Cannot run the test suite: missing connection '{$testDatasource}'. Check main config file.");
}

Now, even when the application tries to use the "wrong" database connection, it will go to the correct database.

I'm not sure there's a change to be made to Fixture Factories unless it's a docs clarification, do you agree @pabloelcolombiano?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants