Let me first confess that I absolutely am a novice in the field of TDD and unit tests. Currently, I am learning to work with TDD to advance myself in my career.
I am reprogramming a program I wrote a year ago using TDD. The program is not that complex. It listens to a websocket (Ratchet), receives information, parses the information, and sends messages through a message bus (RabbitMQ).
I am trying to approach this via TDD so I am starting to write my test first.
My listener class has 4 methods.
Connect (connects to the stream via Ratchet)
Listen (starts listening)
Parse (parses received information)
SendMessage (send a message to the RabbitMQ bus)
The problems I encounter while writing the test first.
How can I mock a Ratchet connection and assert that my function can "connect"
How can I assert that the information received matches my expectations
I find it really hard to find information on this specific subject and I understand that my question is broad. I do not expect a complete solution or answer but hopefully, the terminology to further investigate this subject and lectures/tutorials about writing tests for methods of this kind.
It's hard to argue without having seen any code. From your description it sounds like you have one class that does all the things. This may already be a reason that makes testing harder than it has to be.
When it comes to unit tests, I like to stick with Michael Feathers definition:
A test is not a unit test if:
it talks to the database.
it communicates across the network.
it touches the file system.
it can't run at the same time as any of your other unit tests.
you have to do special things to your environment (such as editing config files) to run it.
Mocking the Ratchet and RabbitMQ connections may seem like a way to make a test fit the above definition, but there is also this other phrase: "Don't mock what you don't own". Although it's not a hard rule, I think it's a pretty good guideline. There are a lot of articles on that topic. This should give you a good overview
Ratchet is a bit special with the server and event loop part, though. But it's well tested, so you don't have to do it. My suggestion: Keep the integration with Ratchet so thin, that there isn't much left to test anyway and just skip the test.
final class MyRatchetApp implements MessageComponentInterface
{
private MessageProcessor $processor;
public function __construct(MessageProcessor $processor)
{
$this->processor = $processor;
}
public function onMessage(ConnectionInterface $from, $msg)
{
$this->processor->handle($msg);
}
// ...
}
This leaves you with a MessageProcessor that you can test in isolation. All of its dependencies are types you own, so you can use mocks, stubs or fake implementations to test their interaction. This implementation is overly simplified and misses stuff like error handling that you certainly want to do and of course want to test.
final class MessageProcessor
{
private MessageParser $parser;
private MessageBroadcaster $broadcaster;
public function __construct(MessageParser $parser, MessageBroadcaster $broadcaster)
{
$this->parser = $parser;
$this->broadcaster = $broadcaster;
}
public function handle(string $rawMessage): void
{
$this->broadcaster->send($this->parser->parse($rawMessage));
}
}
interface MessageParser
{
/**
* #throws BadMessageException
*/
public function parse(string $message): Message;
}
interface MessageBroadcaster
{
/**
* #throws UnsupportedMessageException
* #throws UnroutableMessageException
*/
public function send(Message $message): void;
}
Creating an implementation of the MessageParser should be straightforward and easily unit testable. The RabbitMQ implementation of the MessageBroadcaster would be a perfect candidate for an integration test.
Finally, plug all the concrete implementations together for the real application.
$server = IoServer::factory(
new MyRatchetApp(
new MessageProcessor(
new CommandMessageParser(),
new RabbitMqMessageBroadcaster()
)
),
8080
);
To make sure all parts work together, you can create some end-to-end tests that perform full roundtrips and verify the results. Creating these tests first, allows you to do Double Loop TDD
Related
Trying to get into Unit Testing. Since I'm also implementing CQRS, I was wondering how I would test something like this:
class CommandHandler{
private $repository;
public function __construct( $repository ){
$this->repository = $repository;
}
public function handle( $command ) {
$Entity = new Entity( $command->getSomething() );
$this->repository->add( $Entity );
}
}
Taken that Unit Testing is about testing the public API of my classes, what exactly would I want to test here? That an instance of Entity is passed to the repository?
Trying to get into Unit Testing. Since I'm also implementing CQRS, I was wondering how I would test something like this
The two presentations you want to watch:
Katrina Owen: 467 Tests, 0 Failures, 0 Confidence
Sandi Metz: Magic Tricks of Testing
The TL;DR of both talks is: .
Query messages and command messages sent to the subject of the test are asserted by querying the state of the test subject. Command messages sent by the test subject are verified by expectation -- you confirm that the correct message was sent, without worrying about the effects of that message.
So the answer to your question depends in part on whether or not, for this test, the repository is part of the system under test, or part of the boundary. If the repository is part of the system under test, then you can treat the entire test as verification of an incoming message, and query the state of the repository. If the repository is part of the boundary (if it is provided by the test), then you test the expectation -- was the method on the repository invoked correctly?
In the latter case, there is an additional interesting question: is the need to verify the expectation a code smell? It could be that the test -- by inducing a check against an expectation -- is actually revealing a problem in the design; maybe there should be more instrumentation in the command handler itself, so that the test can query the command handler itself to find out what messages have been sent. Scott Bellware's Doctrine of Useful Objects explores this idea.
I recently tried to improve my unit testing skills and read quite some literature about unit testing and I am also trying to realize what I learned in a php-project I am currently developing with phpunit. But I still have a in my opinion very fundamental question how to unit test methods which interact with objects of other classes or even with other methods of the same class.
Is there some rule of thumb or some help how I can decide what dependencies I should stub/mock and for what dependencies I should simply use a normal object? To clarify my question, here is an example code, with different scenarios:
interface DependencyInterface {
public method dependentMethod() { ... }
}
class Dependency implements DependencyInterface {...}
class ClassUnderTest {
private $dependency
public __construct(DependencyInterface $dependency) {
$this->dependency = dependency;
}
public function methodUnderTest() {
...
$result1 = $this->dependency->dependentMethod();
...
$result2 = $this->otherMethod();
...
$result3 = $this->usedInMultiplePublicMethods();
}
public function otherMethod() {...}
private function usedInMultiplePublicMethods() {...}
}
So my questions are now for a unit test which tests the functionality of the method methodUnderTest, should I:
stub the interface DependencyInterface and inject it in the constructor, or should I simply use an instance of the implementation Dependency?
partially stub the class ClassUnderTest itself to deliver a fixed result for otherMethod, since this maybe very complex method has already its own complete unit tests?
I decided to not unit tests private methods, since they are not part of the interface of the class (I know this is a controversial topic and is not the scope of my question). Do I have now to cover for each public method which uses the private method usedInMultiplePublicMethods all possible effects which may occur in the private method? Or should I only test all possible effects in one of the public methods which uses it and stub the private method in the tests for all other public methods?
I am quite not sure about when to use stubbing/mocking and when not.
The why of mocking is to be able to write unit test that means a test which is: fast, isolated, repeatable, self validating and Thorough and Timely (F.I.R.S.T)
To be able to test a unit/module in isolation you may need to mock/stub any external module (database access, api call, logging system...).
For your points 1 & 2 , rad's answer points to main underlying principles to keep in mind e.g. if you are going to test a logic which is using a database service to fetch data and then doing computations on fetched data, would you mock that database service or use real database service ?
As is clear from objective - you are unit testing logic itself and not database service data fetch so you would mock database service and would assume that database service is giving correct data & you would simply focus on testing logic on computed data. You will have separate tests for database fetching service and that isolation property is all about.
The word unit is significant in that sense that your these tests should be very focused on current logic only by limiting your scope & by not cluttering everything else into it.
This answer is mainly for your points # 3. Its OK to not explicitly test private methods but if you go by basic purpose of unit tests - you wouldn't be much worried about something being private or public. Somewhere down the line, unit testing is for developer self satisfaction too & it would simply make your code more robust if unit tests are written for private methods too.
Just because access level is private doesn't change the basic concept that its a piece of logic that needs testing. Code coverage wise , you might be OK from one public method but I am of view that you should treat calls from different public methods as distinct.
Never forget the basic purpose of unit tests - that you are trying to find errors in your logic, trying to cover all boundary cases & trying to make your code more robust.
I started using unit and functionality test with this project and because of this I have some questions:
Im working with the symfony php framework. And I have a doctrine like LDAP ORM service.
Furthermore I have a user repository (as a service) which depends on the LDAP ORM service, the logger and a validation service.
Now I want to write a unit test for the addUser function of the UserRepo.
It will internally call: getNewUidNumber, userToEntities, doesUserExist and getUserByUid.
My question is:
Should I mock all these internal function to just test the addUser function? Would that be against the unit test idea (just test the API).
Or should I just mock the LDAP ORM service, the Logger, and the validation service, so that the class calls all internal functions? But this would cause a huge test function with a lot of mocking because I have to mock the repositories for all internal repositories calls.
Or should I start the symfony kernel and use the ServiceContainer to use the ORM LDAP service with a real test database. But wouldn't that be a functionally test and not a unit test?
I heard that its bad to have so many dependencies in a test. So I thought it would be bad to use the whole serviceContainer.
Adduser:
public function addUser(User $user)
{
$pbnlAccount = $this->userToEntities($user);
if(!$this->doesUserExist($user)) {
$pbnlAccount->setUidNumber($this->getNewUidNumber());
$this->ldapEntityManager->persist($pbnlAccount);
$this->ldapEntityManager->flush();
}
else {
throw new UserAlreadyExistException("The user ".$user->getUid()." already exists.");
}
return $this->getUserByUid($user->getUid());
}
For more code, like the internal functions:
https://gist.github.com/NKPmedia/4a6ee55b6bb96e8af409debd98950678
Thanks
Paul
First, I would like to rewrite the method a tiny bit, if I may.
public function addUser(User $user)
{
if ($this->doesUserExist($user)) {
throw new UserAlreadyExistException("The user ".$user->getUid()." already exists.");
}
// ... shortened for brevity
$pbnlAccount = $this->userToEntities($user);
$this->ldapEntityManager->persist($pbnlAccount);
}
The other relevant method is:
private function doesUserExist(User $user)
{
$users = $this->ldapRepository->findByUid($user->getUid());
return count($users) === 1;
}
Immediately we can see that we basically have two tests:
We test that the method throws when the user exists
We test that the method persists a PbnlAccount if the user does not exist.
If you do not see why we have these two tests, note that there are 2 possible "flows" in this method: one where the block inside the if statement is executed, and one where it is not executed.
Lets tackle the first one:
public function testAddUserThrowsWhenUserExistsAlready()
{
$user = new User();
$user->setUid('123');
$ldapRepositoryMock = $this->createMock(LdapRepository::class);
$ldapRepositoryMock
->method('findByUid')
->expects($this->once())
->with('123')
->willReturn(new PbnlAccount());
$userRepository = new UserRepository($ldapRepositoryMock);
$this->expectException(UserAlreadyExistException::class);
$userRepository->addUser($user);
}
The second test is left as an exercise for the reader :)
Yes you will have to do some mocking in your case. You wil need to mock the LdapRepository and LdapEntityManager both in this case.
Note 1: this code probably is not runnable, since I do not know the exact details of your code base (and I wrote this off the top of my head), but that is beside the point. The point is that you want to test for the exception.
Note 2:
I would rename your function to createNewPbnlAccountForUser(User $user) which is longer, but more descriptive of what it actually does.
Note 3:
I am not sure why you are returning $this->getUserByUid() since that seems redundant (you already have the User right there), so I am ommitting that case.
You need to mock ldapEntityManager and all repository services but not the internal function.And as you said, don't boot kernel in unit test. So, you should test all cases with success and throwing exception (make sure to check all behaviour)
If you want to perform a unit test, you should mock all collaborators.
Now, entity managers, ldap services and so on should not be mocked (read more here).
Moreover, if you happen to be in a situation where the Arrange part (set mocks, stubs, and so on) is painful and take "a lot" of the test, maybe this is a smell that your class has too many responsibility (is doing too much things).
That said, when I do unit test, I would like the test to fail only for an internal (to the class) reason, not because I've changed a collaborator line that messes up all my tests.
I'm developing a intern app which send some emails in a view methods. Now I'm rebuilding this app TDD-style but I'm stuck at some point. I searched the web for how to test emails with PHPUnit and the solution was, use mock objects. I read some articles and tutorials about it and built a test with a mock object.
The test passed but I don't understand why should use a mock object if you already know the result. I mean, if you already know the result you don't see the failure on your real method? So is this not the way to test methods which use the email function or am I just doing it wrong? Probably the second one haha.
Thanks in advance,
Sjors
When you are unit testing you should be trying to test your method under test (unit of work under test) in isolation from the rest of your system. As soon as you are forced to cross a system boundary, your unit test becomes an integration test. Integration tests tend to be more fragile, harder write and harder to maintain. By isolating the mailing component from the method that creates the mail you no longer need to rely on whether the mailing system is up and running when you unit test runs. You are free to test the logic in the method under test without having to depend on a concrete implementation.
There are two categories of mock tests, interaction tests and state based tests. I'd suggest looking up the difference between the two on google.
I am unfortunately not familiar with the syntax of php to I can only show you an example of what you might be testing in your case using C# syntax, but I am sure you will be able to follow the code.
public interface Mailer
{
void Send(string to, string from, string subject, string body)
}
The above interface would be one that matches the signature of your mailing component that actually sends the mail (it knows about smtp etc).
public class MailSender
{
private IMailer _mailer;
public MailSender(IMailer mailer)
{
_mailer = mailer;
}
public void GenerateMail(string to, string cc, string from, string subject, string body)
{
if (IsValidEmailAddress(to) == false) throw new InvalidEmailAddressException("To");
if (IsEmptySubject(from) == false) throw new EmptySubjectException();
_mailer.Send(to, from, subject, body);
}
private bool IsValidEmailAddress(string emailAddress)
{
// some code/regex to check that the email address is valid
}
private boold IsEmptySubject(string subject)
{
// code to check if the subject is empty
}
}
Now we have logic in the GenerateMail method. You can write unit tests to check if you get exceptions for invalid email addresses and an empty subject and you can also write a unit test to ensure that if you have valid email addresses and a subject that is not empty the send method is called on the mocked mail sender with the correct information in each of the parameters. This type of unit test would an interaction test as the mock is just verifying that the method was called).
Hope this explanation helps.
The reason to use mock objects is isolation. If you write unit tests you want them to be as isolated from other systems or code as possible. Hence the name unit tests. Using mocks allows you to isolate your tests from other systems (email in your case). There is a talk by Misko Hevery http://www.youtube.com/watch?v=acjvKJiOvXw that explains this aspect better than I ever could.
I am new to PHPUnit, infact I started today. And, as far as I have been reading, I came to understand only what this script does.
class UserTest extends PHPUnit_Framework_TestCase
{
protected $user;
// test the talk method
protected function setUp() {
$this->user = new User();
$this->user->setName("Tom");
}
protected function tearDown() {
unset($this->user);
}
public function testTalk() {
$expected = "Hello world!";
$actual = $this->user->talk();
$this->assertEquals($expected, $actual);
}
}
For this class:
<?php
class User {
protected $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function talk() {
return "Hello world!";
}
}
Ok, so I have established that the test returns a Ok/Fail statement based on the equality of the test, but what I am looking for is more. I need an actually ways to test a more complex class that, its outcome, un-like in this example can not be guessed easily.
Say, I write a script that does polling. How would, or in what ways can I test if the methods/classes can work? The above code only shows if a methods outcome will only be 'Hello World' But, that is too easy thing to test, because I need to test complex things and there aren't much tutorials for that.
Testing a class in a unit test is supposed to NOT be complex.
And the reason for this to be true is that in a well designed system, you can test that class in isolation, and do not add the complexity of the system behind that class - because that system does not exist during the test, it is mocked.
The classic example for this is that a User class usually asks a database for info, but in the test case, that database is really hard to set up, prepare with data, and destroy afterwards. And using a real database also slows things down. So you design the User class to accept a database object from the outside, and in the test case you give to it a mock object of the database.
That way, it is really simple to simulate all kinds of return values from the database, and additionally you can check whether the database object gets the right parameters without the complexity of dealing with a real database.
You can only do proper mock object injection if your classes are designed to allow dependency injection. That is a principle to NOT create objects inside of other objects, but require the outside world to supply them. Just have a quick look at some video for explanation:
Dependency Injection - Programming With Anthony (Jan 2013)
And remember that creating good tests needs some experience. Good for you to start experimenting.
But, that is too easy thing to test, because I need to test complex things and there aren't much tutorials for that.
The more complex, the more complex the tests are. It's a bit like a snake biting in its own end. You normally want to prevent that, so to make it golden: Write simple tests to ensure that a complex software is tested and runs.
This does not always work 100% but it works better than without tests. PHPUnit has been designed for Unit-Tests (Xunit test patterns) but you can also use it to run different tests. For that you group tests. This is done differently by different people depending on different things. For example:
Small tests
Medium tests
Large tests
or (not equivalent):
Unit tests
Integration tests
Acceptance tests
or (perhaps equivalent TestPyramid):
Unit tests
Service tests
UI tests
and what not. When you start with testing, it's probably good to start with the unit-tests and as Sven already answered, keep them simple. Get comfortable with TDD, read some slides and books. And welcome to the world of automating tests.
What is Unit test, Integration Test, Smoke test, Regression Test?
P.S. Yes, simple getter setters are too easy to test. If all a setter does is storing to a private member and the getter gets it back, you can trust that PHP is working, writing a test for that is a waste of time and will only lead to cruft. It clearly shows that somebody wrote the test after the code has been written. Instead write first the test and see it fail (red), than hack together the code as quick as possible to get the test to pass (green). You can improve the code later on as the test already shows it's working.