This presentation is about how to write tests faster, how to use real objects in tests which allow us to refactor a lot of code without worrying about "refactoring" other tests.
Optimizing AI for immediate response in Smart CCTV
How to write not breakable unit tests
1. How to write
not breakable unit tests?
Based on PHPUnit
by Rafal Ksiazek, IT Leader
https://github.com/harpcio
2. Agenda
• The old aproach - mocking everything
– Pros and cons
• The new aproach – using real objects
– Pros and cons
• What we should mock?
• The magic of real objects
• How to mock?
3. The old aproach – mock everything
<?php
namespace Model;
class Car {
public function __construct(EngineInterface $engine) {
$this->engine = $engine;
}
public function run() {
$this->engine->start();
$this->engine->accelerate(10);
}
public function getActualSpeed() {
return $this->engine->getSpeed();
}
}
<?php
namespace TestsModel;
class CarTest {
public function setUp() {
$this->engineMock = $this->getMock(
TestsManualEngine::class
);
$this->testedObject = new Car(
$this->engineMock
);
}
public function testRun() {
$this->engineMock->expects($this->once())
->method(`start`);
$this->engineMock->expects($this->once())
->method(`accelerate`);
$this->testedObject->run();
$this->assertSame(
10,
$this->testedObject->getActualSpeed()
);
}
….
}
4. The old aproach – pros and cons
Pros:
• when class Engine fail, the
CarTest will still be valid
(tests are separated)
Cons:
• writing tests are more time
consuming
• changing class Engine, we
need to change also class
CarTest
• there is no possibility that
we will find not tested
functionality (forgotten or
skiped) in class Engine
5. The new aproach – use real objects
<?php
namespace Model;
class Car {
public function __construct(EngineInterface $engine) {
$this->engine = $engine;
}
public function run() {
$this->engine->start();
$this->engine->accelerate(10);
}
public function getActualSpeed() {
return $this->engine->getSpeed();
}
}
<?php
namespace TestsModel;
class CarTest {
public function setUp() {
$this->testedObject = new Car(
new Engine()
);
}
public function testRun() {
$this->testedObject->run();
$this->assertSame(
10,
$this->testedObject->getActualSpeed()
);
}
….
}
6. The new aproach – pros and cons
Pros:
• writing tests are much faster
• changing class Engine, we
don’t need to change also
class CarTest
• there is possibility that we
will find not tested
functionality (forgotten or
skiped) in class Engine
Cons:
• when class Engine fail, the
CarTest will also fail (but
fixing class Engine will fix
also CarTest)
7. What we should mock?
• We should mock classes that cross the border
of business logic layer, for example:
– Repositories (data access layer)
– File managers (file system layer)
– Connectors (facebook, twitter, google oauth)
8. The magic of real objects
<?php
namespace MyAppTestsRepository;
class UsersArrayRepository implement UsersRepositoryInterface {
public function __construct(array $users = []) {
$this->container = $users;
}
public function delete(User $user) {
foreach($this->container as $key => $elem) {
if ($elem->getId() === $user->getId()) {
unset($this->container[$key]);
return true;
}
}
return false;
}
…
}
9. How to mock
<?php
namespace MyAppService;
class UsersCrud {
public function __construct(
UsersRepositoryInterface $usersRepository
) {
$this->usersRepository = $usersRepository;
}
public function delete($userId) {
$user = $this->usersRepository->find($userId);
if (!$user) {
throw new UserNotFoundException();
}
return $this->usersRepository->delete($user);
}
}
<?php
namespace MyAppTestsService;
class UsersCrud Test {
public function setUp() {
$this->testedObject = new UsersCrud (
new UsersArrayRepository(
12 => new User()
)
);
}
public function testDelete_WhenSuccess() {
$result = $this->testedObject->delete(12);
$this->assertTrue($result);
}
….
}
10. Summary
• changing name of method „delete” to
„deleteOnlyDeacitivatedUser” in UsersRepository will force
change in the class „UsersCrud” and „UsersRepositoryTest”,
but not in the „UsersCrudTest”
• so .. we can do a lot of refactoring without worrying about
tests, hundreds of classes which use UsersRepository uwill
change automatically by IDE and tests of these classes will still
be valid
• thx for watching!