Skip to content

Commit ba54f28

Browse files
Merge pull request #76 from mage-os/DavidLambauer-patch-9
2 parents 22c3845 + 76e34a3 commit ba54f28

File tree

1 file changed

+207
-99
lines changed

1 file changed

+207
-99
lines changed

unit-testing.md

Lines changed: 207 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,265 @@
1-
# Unit Testing Documentation
1+
# Unit Testing in Magento 2
22

33
[TOC]
44

5-
## Introduction
5+
## Overview
66

7-
Unit testing is an essential practice in software development that involves testing individual units or components of
8-
code to ensure their correctness. In this documentation, we will explore the fundamentals of unit testing, its benefits,
9-
and how to write effective unit tests for PHP and Magento 2.
7+
Unit testing is a critical part of software development. It involves testing individual units of code (such as functions
8+
or methods) to ensure they perform as expected. In Magento 2, PHPUnit is the testing framework used for writing and
9+
executing unit tests.
1010

11-
## Benefits of Unit Testing
11+
Magento 2 has a solid structure for unit tests that separates them from other types of tests (like integration,
12+
functional, and performance tests). In a typical Magento 2 module, the unit tests are located in the `Test/Unit`
13+
directory of the module.
1214

13-
Unit testing offers numerous benefits, including:
15+
## Setup and Configuration
1416

15-
1. **Bug Prevention**: By testing individual units of code, potential bugs and errors can be identified and fixed early
16-
in the development process, reducing the overall cost and effort required.
17+
Before you begin, ensure you have PHPUnit installed. Magento 2 recommends using the latest stable PHPUnit version
18+
supported by your PHP installation.
1719

18-
2. **Code Maintainability**: Unit tests act as a safety net when making changes to the codebase. They provide assurance
19-
that existing functionality will not break due to modifications, making it easier to refactor and improve code.
20+
You can install PHPUnit globally or as a part of your project via Composer. The following command installs PHPUnit
21+
globally:
2022

21-
3. **Documentation**: Unit tests serve as living documentation, providing examples of how the code should be used and
22-
the expected behavior of its components.
23+
```bash
24+
composer global require phpunit/phpunit
25+
```
26+
27+
After installing PHPUnit, validate the installation by checking the version:
2328

24-
4. **Rapid Feedback**: Unit tests enable quick feedback on code changes, as they can be executed independently and do
25-
not require the entire application to be running.
29+
```bash
30+
phpunit --version
31+
```
2632

27-
5. **Collaboration**: Unit tests facilitate collaboration among developers by providing a common understanding of code
28-
functionality and behavior.
33+
## Creating a Unit Test
2934

30-
## Unit Testing in PHP
35+
In this section, we'll create a simple unit test for a hypothetical SampleModule in Magento 2.
3136

32-
### PHPUnit
37+
1. Navigate to your module's directory and create a new Test/Unit directory if it doesn't exist.
3338

34-
PHPUnit is a widely used unit testing framework for PHP. It provides a rich set of assertions, test runners, and other
35-
utilities to aid in writing effective unit tests. Let's look at some examples of how to write unit tests using PHPUnit.
39+
```bash
40+
mkdir -p app/code/Vendor/SampleModule/Test/Unit
41+
```
3642

37-
To begin, create a new test class, `MyClassTest`, that extends the `PHPUnit\Framework\TestCase` class:
43+
2. Inside the `Test/Unit` directory, create a new test class. In this case, we'll create `SampleTest.php`:
3844

3945
```php
40-
use PHPUnit\Framework\TestCase;
46+
<?php
47+
declare(strict_types=1);
48+
49+
namespace Vendor\SampleModule\Test\Unit;
4150

42-
class MyClassTest extends TestCase
51+
class SampleTest extends \PHPUnit\Framework\TestCase
4352
{
44-
// Test methods will be written here
53+
public function testSampleMethod(): void
54+
{
55+
$this->assertEquals(10, 5 + 5);
56+
}
4557
}
4658
```
4759

48-
#### Asserting Equality
60+
In this example, `testSampleMethod` is a simple test case that asserts whether `5 + 5` equals `10`.
4961

50-
PHPUnit provides various assertion methods to validate expected outcomes. One common assertion is to check for equality
51-
between expected and actual values. For example:
62+
## Running Unit Tests
5263

53-
```php
54-
public function testAddition()
55-
{
56-
$result = 2 + 2;
57-
$this->assertEquals(4, $result);
58-
}
64+
To run the unit test, use the phpunit command from the root directory of your Magento 2 installation:
65+
66+
```bash
67+
./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code/Vendor/SampleModule/Test/Unit
5968
```
6069

61-
In the above example, the `assertEquals` method checks whether the `$result` variable is equal to `4`.
70+
This command tells PHPUnit to use the configuration specified in phpunit.xml.dist and run the tests located
71+
in `app/code/Vendor/SampleModule/Test/Unit`.
6272

63-
#### Mocking Dependencies
73+
To run an entire suite (i.e., all tests in a directory), specify the directory path:
6474

65-
When testing code that has dependencies, it's often necessary to create mocks or stubs to isolate the unit being tested.
66-
PHPUnit provides a `getMock()` method to create mock objects.
75+
```bash
76+
./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code/Vendor/SampleModule/Test/Unit
77+
```
6778

68-
```php
69-
public function testSomethingWithMock()
70-
{
71-
$dependencyMock = $this->getMockBuilder(DependencyClass::class)
72-
->getMock();
79+
The output should look something like this:
7380

74-
// Use the mock object within the test
75-
}
81+
```bash
82+
PHPUnit X.X.X by Sebastian Bergmann and contributors.
83+
84+
. 1 / 1 (100%)
85+
86+
Time: X ms, Memory: X MB
87+
88+
OK (1 test, 1 assertion)
7689
```
7790

78-
In the above example, the `getMockBuilder()` method creates a mock object of the `DependencyClass`. This allows the test
79-
to control the behavior of the dependency and focus solely on the unit being tested.
91+
## Run Unit Tests within PHPStorm
92+
93+
To run tests within PHPStorm, follow these steps:
94+
95+
Open the `Settings/Preferences` dialog (`Ctrl+Alt+S`), go to `Languages & Frameworks | PHP | Test Frameworks`.
96+
Click `+`, and choose `PHPUnit Local`.
97+
Configure the PHPUnit library and settings.
98+
After the configuration, go to your test, right-click on it and select Run `test-name`.
99+
100+
## Write Testable Code
101+
102+
Writing testable code involves designing your code in a way that makes it easier to isolate and test individual units.
103+
Some tips for writing testable code include:
80104

81-
For more advanced mocking scenarios, PHPUnit also supports the use of mock libraries such as Mockery and Prophecy.
105+
**Single Responsibility**: Each method/class should have a single responsibility. It should do one thing and do it well.
106+
**Avoid Static Methods**: Static methods can't be mocked or stubbed, making them difficult to test.
107+
**Dependency Injection**: Using dependency injection helps make your code more flexible and easier to test.
82108

83-
## Unit Testing in Magento 2
109+
## Best Practices
84110

85-
### Magento Testing Framework (MTF)
111+
**Use Descriptive Test Method Names**: The method name should describe what the test does. For example,
112+
`testUserIsCreatedWithValidInput` is descriptive and you can understand what the test does by looking at its name.
113+
**One Assertion Per Test Method**: Ideally, each test should only have one assertion. This makes the tests more readable
114+
and
115+
errors easier to pinpoint.
116+
**Don't Test Everything**: It's important to note that not all code needs to be tested. If it's already being tested
117+
elsewhere or it's part of the framework's functionality, there's usually no need to test it again.
86118

87-
Magento 2 provides a dedicated testing framework called Magento Testing Framework (MTF) for writing unit tests. MTF
88-
extends PHPUnit and introduces additional features specific to Magento.
89119

90-
To write unit tests in Magento 2, follow these steps:
120+
## Mocking
91121

92-
1. Create a new test class that extends the `Magento\FunctionalTestingFramework\TestCase\TestCase` class:
122+
Mocking is a process used in unit testing when the unit of code has external dependencies. A mock object is a dummy
123+
implementation that simulates the behavior of a real object.
93124

94125
```php
95-
use Magento\FunctionalTestingFramework\TestCase\TestCase;
126+
<?php
127+
namespace Vendor\SampleModule\Test\Unit;
96128

97-
class MyMagentoTest extends TestCase
129+
use PHPUnit\Framework\TestCase;
130+
use PHPUnit\Framework\MockObject\MockObject;
131+
use Vendor\SampleModule\Model\Sample;
132+
133+
class SampleTest extends TestCase
98134
{
99-
// Test methods will be written here
135+
/**
136+
* @var MockObject
137+
*/
138+
private $sampleMock;
139+
140+
protected function setUp(): void
141+
{
142+
$this->sampleMock = $this->getMockBuilder(Sample::class)
143+
->disableOriginalConstructor()
144+
->getMock();
145+
}
146+
147+
public function testSampleMethod()
148+
{
149+
$this->sampleMock->method('getNumber')
150+
->willReturn(5);
151+
152+
$this->assertEquals(10, $this->sampleMock->getNumber() + 5);
153+
}
100154
}
101155
```
102156

103-
2. Use the Magento testing APIs to interact with the Magento system and test the desired functionality.
157+
In this example, `Sample::class` is a dependency of the unit being tested. We create a mock for this dependency and
158+
define
159+
its behavior to return `5` when getNumber is called.
160+
161+
## DocBlock Annotations
162+
163+
DocBlocks or PHPDoc comments are used to provide metadata for your codebase. These comments can be parsed by tools to
164+
generate human-readable documentation.
165+
166+
PHPUnit uses these annotations to control the behavior of your tests. Here are some commonly used annotations:
167+
168+
- `@test`: This annotation can be used to mark a method as a test method.
169+
- `@dataProvider`: This annotation can be used to specify a method that returns data for a test.
170+
- `@depends`: This annotation can be used to specify that a test depends on another test.
171+
- `@group`: This annotation allows you to group tests together so you can run a set of tests separately from others.
172+
173+
Example:
104174

105175
```php
106-
public function testMyCustomModule()
176+
/**
177+
* @test
178+
* @dataProvider additionProvider
179+
*/
180+
public function testAdd($a, $b, $expected)
181+
{
182+
$this->assertEquals($expected, $a + $b);
183+
}
184+
185+
public function additionProvider()
107186
{
108-
$this->loginAdminUser('admin', 'password');
109-
$this->openAdminPage('my_custom_module');
110-
$this->seePageTitle('My Custom Module');
111-
// Additional assertions and interactions
187+
return [
188+
[1, 2, 3],
189+
[0, 0, 0],
190+
[-1, -1, -2],
191+
];
112192
}
113193
```
114194

115-
In the above example, the test interacts with the Magento system by logging in as an admin user, opening an admin page,
116-
and asserting the expected page title.
117-
118-
### Data Fixtures
119-
120-
Magento 2 allows the creation of data fixtures to provide consistent test data for unit tests. Data fixtures are defined
121-
in XML files and can be used to set up test scenarios.
122-
123-
```xml
124-
<testData>
125-
<fixture>
126-
<name>my_module_data</name>
127-
<data>
128-
<table_name>
129-
<row>
130-
<column1>value1</column1>
131-
<column2>value2</column2>
132-
</row>
133-
</table_name>
134-
</data>
135-
</fixture>
136-
</testData>
195+
In this example, `@test` marks `testAdd` as a test method, and `@dataProvider` specifies `additionProvider` as the
196+
method
197+
providing data for the `testAdd` test.
198+
199+
This concludes our in-depth guide on unit testing in Magento 2. Remember that good tests can help you catch bugs early,
200+
make your codebase more maintainable, and save you, your team, and your users a lot of trouble down the line. Happy
201+
testing!
202+
203+
204+
## Code Coverage
205+
206+
Code coverage is a measure that describes the degree to which the source code of a program is executed when a particular
207+
test suite runs. To generate a code coverage report, PHPUnit needs `Xdebug` or `pcov` installed.
208+
209+
You can enable code coverage in the PHPUnit configuration file (phpunit.xml) or run the command:
210+
211+
```bash
212+
./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist --coverage-html coverage app/code/Vendor/SampleModule/Test/Unit
137213
```
138214

139-
In the above example, a fixture named `my_module_data` is defined, which inserts a row into the `table_name` table with
140-
specified column values.
215+
This command will generate an HTML report in the `coverage` directory.
141216

142-
Fixtures can be used within a test using the `loadFixture()` method provided by MTF:
217+
## Run Unit Tests in CI
143218

144-
```php
145-
public function testMyModuleWithFixture()
146-
{
147-
$this->loadFixture('my_module_data');
148-
// Additional test logic using the fixture data
149-
}
219+
### GitHub Actions
220+
221+
In your `.github/workflows` directory, create a new file called `run-tests.yml` and populate it:
222+
223+
```yaml
224+
name: Run PHPUnit Tests
225+
226+
on: [ push, pull_request ]
227+
228+
jobs:
229+
build:
230+
runs-on: ubuntu-latest
231+
232+
steps:
233+
- uses: actions/checkout@v3
234+
235+
- name: Validate composer.json and composer.lock
236+
run: composer validate
237+
238+
- name: Install Dependencies
239+
run: composer install --prefer-dist --no-progress --no-suggest
240+
241+
- name: Run PHPUnit
242+
run: ./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code/Vendor/SampleModule/Test/Unit
243+
```
244+
245+
### GitLab Pipelines
246+
247+
In your .gitlab-ci.yml, add:
248+
249+
```yaml
250+
stages:
251+
- test
252+
253+
phpunit:
254+
stage: test
255+
image: php:latest
256+
script:
257+
- composer install
258+
- ./vendor/bin/phpunit -c dev/tests/unit/phpunit.xml.dist app/code/Vendor/SampleModule/Test/Unit
150259
```
151260
152-
## Conclusion
261+
## Summary
153262
154-
Unit testing plays a crucial role in software development, enabling developers to validate the correctness of individual
155-
units of code. By incorporating unit testing into your PHP and Magento 2 projects, you can increase code quality, reduce
156-
bugs, and improve maintainability. Use this documentation as a guide to get started with unit testing and leverage the
157-
benefits it offers. Happy testing!
263+
Unit testing is a vital part of the Magento 2 development process. It helps ensure that your code functions as expected,
264+
which can help reduce bugs and issues in the future. By using PHPUnit, Magento 2 developers can write and run unit tests
265+
to validate their code throughout the development process.

0 commit comments

Comments
 (0)