PHPackages                             superreal/srunit - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. superreal/srunit

ActiveLibrary[Testing &amp; Quality](/categories/testing)

superreal/srunit
================

Library containing environment especially for writing unit tests for OXID modules.

v0.9.1(11y ago)51.2k3[2 issues](https://github.com/superReal/srunit/issues)GPL-3.0+PHP

Since Sep 25Pushed 10y ago7 watchersCompare

[ Source](https://github.com/superReal/srunit)[ Packagist](https://packagist.org/packages/superreal/srunit)[ RSS](/packages/superreal-srunit/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (2)Dependencies (5)Versions (4)Used By (0)

sR Unit
=======

[](#sr-unit)

This package contains all necessary components to write unit tests for oxid modules - including bootstrapping and mocking capabilities.

Installation
------------

[](#installation)

Just add the following requirement to the `composer.json` of your project, and call `composer update superreal/srunit`

```
"superreal/srunit": "0.10.*@dev"

```

All required packages will be installed automatically (e.g. PHPUnit, Mockery).

Setup Unit Tests for Module
---------------------------

[](#setup-unit-tests-for-module)

The following steps are needed to setup unit testing for your module.

### Module Configuration

[](#module-configuration)

Add phpunit.xml to module-root with at least the following content:

```

        tests

```

### Project/Shop Configuration

[](#projectshop-configuration)

Add phpunit.xml to shop-root with the following content:

```

        tests

        modules

```

Once you've done that you can run phpunit from your shop root, and all tests will be performed (project- and module-related).

**Note:** Adding the `TestListener` has the effect, that after each test the expectations are verified.

### Bootstrap

[](#bootstrap)

Your tests should be placed in `tests`. Under tests you place your `bootstrap.php` with the following content:

```
\SrUnit\Bootstrap::create(__DIR__)->bootstrap();

```

The bootstrapping process will retrieve all needed directories on its own, and will load the `composer autoloader`, and a `custom autoloader` for the classes of your OXID module - based on the configuration in your `metadata.php`.

This also applies when you're running your tests from your shop-root. In that case the bootstrapping will set up autoloading for all tests, even the tests within your modules. But this is based on the correct configuration of your module. Meaning: the module is responsible to set up the autoloading correct.

This is done first of all by adding the "autoload" configuration in your `composer.json`. If you need to define more than one directory for one Namespace (e.g. for your tests) you have to do it there. Additional to that, the `metadata.php` is taken into account. Either in the `"extend"`, and/or the `"files"` section.

### TestCases must derive from SrUnit\\TestCase

[](#testcases-must-derive-from-srunittestcase)

All of your TestCases should extend the SrUnit\\TestCase in order to enable the OXID related functionatities or convience-methods.

#### Loading OXID

[](#loading-oxid)

OXID is **not** loaded by default. Basic functionalities like `oxNew()` or `oxDb::getDb()` are emulated. You can control their behaviour by using mocks.

In case you need OXID loaded (e.g. for integration tests) you can load OXID by adding a group annotation:

```
@group needs-oxid

```

The `TestListener` will activate the loading of OXID for the particular tests, and enabling/disabling the needed module `superreal/srunit-module`. This module has to be required in your `composer.json` as well - otherwise the test will die with an Exception.

Running *phpunit*
-----------------

[](#running-phpunit)

When you set up your environment as mentioned before, you can run `phpunit` either in your shop-root, or in a specific module. But in order to have the autoloading setup correctly you need to run the phpunit that is *shipped with composer*. Depending on your setup you can use the following calls:

```
bin/phpunit
vendor/bin/phpunit

```

Using the Mock-Factory
----------------------

[](#using-the-mock-factory)

The Factory supports you on creating mocks to replace the dependencies of your SUT. It takes care of OXID-related requirements also, with a easy understandable fluent interface.

When you call the getMock() method at the end of the method-chain you will get back a Mockery\\MockInterface with small additions (e.g. implementsArrayAccess()).

The underlying libraries is Mockery, even if it is not called directly, you will get back the Mock object from Mockery.

### Creating Simple Mocks

[](#creating-simple-mocks)

Simple Mock, simple call:

```
$mock = Factory::create('TestClass')->getMock();

```

Afterwards you can define the behaviour of the mock by simply use the Mockery methods:

```
$mock->shouldReceive('getParam')->andReturn('a-value')

```

### Testing OXID Extensions

[](#testing-oxid-extensions)

When it comes to extending OXID core classes (e.g. oxArticle) you might need to test whether your implementation is correct or not. In case you don't need to have the whole OXID stack to test your implementation, you can mock just the `_parent` class by doing this:

```
$mock = Factory::createParentClass('\SrMyExtensionOxArticle_parent')->getMock();

```

Be aware that this call will actually define a class `SrMyExtensionOxArticle_parent` with the behaviour you will apply on it.

Meaning: After the initial instantiation the class it will have the same behaviour for the whole PHP process. Whenever you'll create a new instance, you will get the same results. When you need different behaviour for different tests you have to run your tests in isolation by adding the following annotation to your test method:

```
/**
 * @runInSeparateProcess
 */
public function testInSeparateProcess()
{
    // ...
}

```

### Integration Tests with Usage of OXID-Factory

[](#integration-tests-with-usage-of-oxid-factory)

In case you need to test the integration of your module or you'd like to use the OXID factory in order to have the whole stack available, you can use the following call:

```
$mock = Factory::create('\oxArticle')
    ->registerForOxNew()
    ->getMock();

```

This call will create a mock, and will also register this mock-object to be retrieved on every call of `oxNew('oxArticle')`. This is pretty usefull when you have dependant classes that make usage of oxNew() calls very often, and you're not able to change this behaviour from the outside.

### Provisioned Mocks

[](#provisioned-mocks)

Often you don't want to create mocks, and apply the same behaviour over and over again. For this case you can use provisioning to get back mocks with default values/stubs.

```
$mock = Factory::create('\oxArticle')
    ->useProvisioning()
    ->getMock();

```

In some cases this call will lead to an Exception because no provisioner is available. You need to implement a provisioner on your own then.

### Mocks with Interfaces

[](#mocks-with-interfaces)

You can define the interfaces a mock should implement, like this:

```
$mock = Factory::create('TestClass')
    ->implementsInterface('\Iterator')
    ->implementsInterface('\Mockable')
    ->getMock();

```

Be Aware: the interface must exist!

For some Iterator interfaces there is already a stubbing mechanism of the particular methods enabled:

- Iterator
- ArrayAccess

For those interfaces it is needed to pass on data to the method in order to have the desired behaviour:

```
$data = array('foo', 'bar', 'barz');

$mock = Factory::create('TestClass')
    ->implementsInterface('\Iterator', $data)
    ->getMock();

```

You will iterate over the given data, when you use this mock.

Mocking the Filesystem
----------------------

[](#mocking-the-filesystem)

If your SUT depends on the filesystem, and you want to set up a certain test environment, you can use the filesystem-utility.

### Create the Filesystem

[](#create-the-filesystem)

You are able to choose between a virtual or a physical filesystem. Whereas a virtual filesystem is suitable for most of the cases, sometimes it's necessary to go with a physical filesystem (e.g. if you're dealing with symlinks).

*(The virtual filesystem is realized with [vfsStream](http://vfs.bovigo.org/))*

```
$fs = new VirtualFilesystem($rootDir);

```

or

```
$fs = new Filesystem($rootDir);

```

#### By using `TestCase::createFilesystem()`

[](#by-using-testcasecreatefilesystem)

Within your `TestCase` you can call the method `createFilesystem()`. You're able to choose between a virtual or a physical filesystem by passing on a second parameter. Either way, the usage is the same.

```
$fs = $this->createFilesystem('/tmp', FilesystemInterface::VIRTUAL);

```

or

```
$fs = $this->createFilesystem('/tmp', FilesystemInterface::PHYSICAL);

```

*Info: Even if you choose a physical filesystem and define `/tmp` as the root-directory, the created environment is not written to system temporary directory `/tmp`.*

### Create Directories and Files

[](#create-directories-and-files)

You will get back an object which implements the `FilesystemInterface`:

- `createDirectory()`
- `createFile()`
- `tearDown()`

You can create directories and files with fullpaths.

```
$filesystem->createDirectory('path/to/diretory');
$filesystem->createFile('path/to/file.txt');

```

In return you will get an `SplFileInfo` object you can work with.

### Clean up Environment

[](#clean-up-environment)

In order to control the clean up process of your tests, you need to call the `tearDown()` method (e.g. in your `TestCase`'s `tearDown()` method).

```
protected function tearDown()
{
    $this->filesystem->tearDown();
}

```

Actually this is only needed if you're using the physical filesystem, because the virtual one only exists in memory and is removed automatically. But in order to keep it consistent: stick to this approach.

###  Health Score

27

—

LowBetter than 49% of packages

Maintenance12

Infrequent updates — may be unmaintained

Popularity21

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~95 days

Total

4

Last Release

3965d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8b235d573493ac94ed927ac770458e94b3a6f5112d341ed338f3c4db4ca1640f?d=identicon)[jenswiese](/maintainers/jenswiese)

### Embed Badge

![Health badge](/badges/superreal-srunit/health.svg)

```
[![Health](https://phpackages.com/badges/superreal-srunit/health.svg)](https://phpackages.com/packages/superreal-srunit)
```

###  Alternatives

[orchestra/testbench

Laravel Testing Helper for Packages Development

2.2k39.1M32.1k](/packages/orchestra-testbench)[jasonmccreary/laravel-test-assertions

A set of helpful assertions when testing Laravel applications.

3513.9M32](/packages/jasonmccreary-laravel-test-assertions)[drupal/core-dev

require-dev dependencies from drupal/drupal; use in addition to drupal/core-recommended to run tests from drupal/core.

2021.0M277](/packages/drupal-core-dev)[webmozarts/strict-phpunit

Enables type-safe comparisons of objects in PHPUnit

31252.7k5](/packages/webmozarts-strict-phpunit)[lastzero/test-tools

Increases testing productivity by adding a service container and self-initializing fakes to PHPUnit

2244.3k13](/packages/lastzero-test-tools)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
