X
X
xfg2015-06-23 17:19:13
Yii
xfg, 2015-06-23 17:19:13

How to write unit tests correctly?

Hello. They write on the Internet that the method under test must always be isolated from any dependencies, otherwise the method under test, in the course of its execution, will pull up external methods of the same or another class, which contradicts unit testing, since in this case not one entity will be tested, but the interaction between multiple entities. Is this statement true?
I had this question after learning about unit tests in the Yii framework. LoginFormTest::testLoginNoUser() calls the LoginForm::login() method , which internally calls LoginForm::getUser()and the execution process goes beyond the LoginForm class into the User class. As a result, it turns out that LoginForm::login(), LoginForm::getUser() and User::findByUsername() are tested. But why. After all, I shouldn't want to test the operation of the User::findByUsername() method here, I want to do it in another test.
Can this be called unit testing or is it still integration testing?
Now it turns out that User::findByUsername() is implicitly tested in LoginFormTest and will also be explicitly tested in UserTest, is that normal? And if so, is it okay to dump everything in one directory and unit tests and integration tests? Where is the edge.

Answer the question

In order to leave comments, you need to log in

4 answer(s)
H
He11ion, 2015-06-23
@xfg

Usually there are mocks and stubs for unit testing tasks.
Yes, it is very similar to integration, however, it is not critically different from a unit, so I don’t see a big problem here.

T
thyratr0n, 2015-06-26
@thyratr0n

It is not clear what you wanted to receive in response, because you did not ask a clear question.
First, of course, you "luckily" chose the first version of Yii as an example. This framework is not very suitable for making "testable" code, because it contains a large amount of "magic" at all stages of functioning. Don't believe? Try to build something based on CActiveRecord and file unit tests. You will get anything, but not what you need.
The most important aspect of unit testing is that one test (method of the test class) should solve exactly one task, and not check the whole subsystem in the background.
On one of my projects, I solved this problem by writing a whole bunch of stubs.
Secondly, it all depends on the tasks. Roughly speaking, unit tests test the code as it is. The next level of tests (I'm not going to name it, because I constantly confuse them) checks business scenarios. And the highest level checks the whole thing in conjunction with external interfaces.
And thirdly, one test should not include the code of other tests, because each test should be isolated, assuming that all other code works. That is why, when you write, for example, a test for a small model, you do not need to drive work with the database: it is considered that the entire subsystem of interaction with the database is _working_.
Yes, and more. They often confuse mocks and stubs.
A stub is a stub - code that "silences" the execution of another. The purpose of a stub is to test what _what_ an object returns in its operation. In your case, you need some kind of object that will not allow the code to be executed further LoginForm::login(). How to do it? With static code typing - no way. We need an add-on that will dynamically use these objects so that they can be replaced with stubs.
A mock is an add-on to an object under test, the purpose of which is to test how the object works inside, which is why mocks are always built on Reflection.

M
matperez, 2015-06-23
@matperez

The good thing about unit tests is to use stubs instead of other parts of the system that you don't want to test right now. These stubs need to be slipped into the model under test, either through the DI container at the test environment configuration stage, or through an explicit method in the model.
Example:
In the LoginForm, you can make an explicit dependency on something like UserFinder, which can find a user by email or login. Instead, you can slip an object that returns a very specific user model.
We also don’t want to test the user model in this test, so UserFinder should not return the real User model, but again some kind of stub that returns the data specified in advance.
The easiest way to create stubs is to use mocks from phpunit. Through this thing, you can also mock a separate method of the entire class. For example, you can make the findUser method in the LoginForm model immediately give the "correct" user and thereby remove the UserFinder from consideration.
In general, what code - such and tests. If the code allows you to easily substitute dependencies, you can test it in a unit style, otherwise you get under-international tests. Accordingly, if you write tests before the code itself, you will naturally get more testable code.

N
nonlux, 2015-06-23
@nonlux

They write on the Internet that the method under test must always be isolated from any dependencies, otherwise the method under test, in the course of its execution, will pull up external methods of the same or another class, which contradicts unit testing, since in this case not one entity will be tested, but the interaction between multiple entities.
Is this statement true?
I think it's generally true. To solve this problem, duplicates were invented ( Google showed it in Russian ).
That is, if you write a clean unit test for the LoginForm, then in my opinion the best solution is to replace it with "mocks":
- YII:$app->:$app->userwhich will return for Yii::$app->user->login()false
- app\models\Userwhich will return for User::findByUsername()null
Unfortunately, I do not work with YII and therefore a more detailed implementation of this now I can't write.
Can this be called unit testing or is it still integration testing?
And if so, is it okay to dump everything in one directory and unit tests and integration tests? Where is the edge.

The edge is determined by the developer, his team and the methodologies they use.
For example, if you write tests first, and then implement the functionality, then they look cleaner and look like unit tests.
If you write code first, and then cover it with tests, they are already more like integration ones, as you say. Actually it is in your example.
I personally came to the conclusion that such theoretical tinsel bothers me and I do not single out tests for such categories. For myself, I select tests that test the business logic of the application and tests (specifications) for the code.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question