6

I'm a newcomer to PHPUnit (and unit testing in general). I want to work on a test suite that developers can run locally, but can also be run in our integration system (Codeship). I understand that it is possible to use an in-memory database, but it seems like that relies on the migrations, which we are not using (doesn't seem to handle views, stored procedures, functions, triggers, etc very well?).

What's the best way (place in Laravel) to 1) create a database in memory and seed the database with default data (to be used for ALL test)?

QuietSeditionist
  • 703
  • 4
  • 8
  • 18
  • Possible duplicate of [How do I make a MySQL database run completely in memory?](https://stackoverflow.com/questions/10692398/how-do-i-make-a-mysql-database-run-completely-in-memory) – hakre Jul 01 '19 at 00:03
  • 1
    @harkre, definitely no duplicate but it can provide some hints on how to achieve the result. – 8ctopus Mar 23 '23 at 05:42

2 Answers2

7

You can use SQLite.

From the docs:

An SQLite database is normally stored in a single ordinary disk file. However, in certain circumstances, the database might be stored in memory.

Add this to the config/database.php file:

'sqlite_testing' => [
    'driver'   => 'sqlite',
    'database' => ':memory:',
    'prefix'   => '',
],

On the phpunit.xml file, under <php> node:

<env name="DB_CONNECTION" value="sqlite_testing" />

Read more here.

Other solution

Create a testing database on your storage/ folder, with the name database.sqlite or if you want another name or another location you have to change the configs on the config/database.php file, these are the default configs:

'sqlite' => [
    'driver'   => 'sqlite',
    'database' => storage_path('database.sqlite'),
    'prefix'   => '',
],

Use this command to run your migrations:

php artisan migrate --database=sqlite

Or add this line to the .env file:

DB_CONNECTION=sqlite

Your application is using sqlite for phpunit.

Now you can run your migrations and test. After that, just change the DB_CONNECTION to the database you are using for your project.

Mateus Junges
  • 2,559
  • 1
  • 12
  • 24
  • 2
    Thanks! It looks like I will run into a problem here because our database uses tons of different datatypes and apparently other structures that are not supported by sqllite. So it seems I will have to actually have a mysql database available.. – QuietSeditionist Jun 19 '19 at 19:43
  • You can also use [travis ci](https://travis-ci.org) to your tests. If you don't want to use sqlite, travis ci allows you to configure a database like mysql and pgsql (which i normally use) for your tests. For tests in your local machine, i think you can use mysql without problems. – Mateus Junges Jun 19 '19 at 19:48
7

This isn't an answer you may be looking for but more of an alternative solution to consider.

From my experiences, I've found the path of least resistance is to actually mock your models (or the query builder if you are using that) and have them return the results you need them to.

When developing tests, you should always be thinking about what dependencies can be removed entirely from the test so you can keep your focus on only what you are trying to test at that time.

There are also likely going to be nuances (most likely very minor) between running an in-memory database vs how your production database will be working which will ultimately hurt the integrity of your tests and may end up even giving you false-positives. This will be especially true if you are using the query builder and you may end up needing to actually develop different queries (different syntaxes between MySQL and sqlite for example) depending on what environment you the query itself to even work.

user1669496
  • 32,176
  • 9
  • 73
  • 65
  • 1
    So the database interactions just don't get tested at all in this scenario? – QuietSeditionist Jul 17 '19 at 18:29
  • 1
    It really depends. If you've used the query builder to write your own queries, then you probably do want to test that somehow and proceed with an actual database (I'd recommend setting up a staging db server you can seed and test on). But if you are using Eloquent methods and relationships, you shouldn't need to test those interactions because the testing for those have already been handled by the Laravel devs. Mocking those types of dependencies to return specific data useful to your tests should suffice. – user1669496 Jul 17 '19 at 19:35
  • That's not really a good idea, because when there's a bug in the integration between modules, or even bug within the database API itself, it probably won't be caught. Another disavantage is that mocking database layer in tests very tightly couples the test to the current database implementation, which increases the rigidity of the tests and makes changes harder in the future. – Danon Oct 18 '22 at 10:45
  • A sample on how you do it, would be a great addition to your post. – 8ctopus Mar 23 '23 at 05:41
  • I say for unit tests go for mocking (_what this answer suggests_), for integration tests have a running (test) database. – ozanmuyes Mar 30 '23 at 09:23