It was a regular weekday, I started writing another regular test, but when I wrote the name of the test, something went wrong:

testCustomerRedirectUrlAfterSelectLastTransactionProcessShouldContainsUrlToConfirmRecurringProfilePageWhenTransactionTypeIsCreateRecurringProfile 

Approximately in the middle I began to realize that something was going wrong, but I did not stop and added the name of the test to the end. Colleagues did not appreciate such a long name of the test, but I like it. Now something like testCorrectChangeCustomerRedirectUrl , and the details of what the test is testing are hidden inside the test.

I can not understand this lvlap, or I'm tired in the evening.

Question: What are the criteria for choosing a good test name?

UPD: Please provide the test names from your projects.

  • ten
    To the children who want to close the question, I inform you that this is an important topic, which is discussed in books on TDD, naming patterns are created and applied, it is discussed that test methods are used in a different context than the methods of the project code. Do you want runet users to collect this information? You are welcome. - Andrey Kolomensky
  • 6
    SO is not a place for discussion. (there are chats for discussions) - Abyx
  • 15
    Again the shutters ponabezhali. Gentlemen, well, let's turn on the head. If the question seems to you incomprehensible just because you are not in the subject line, this does not mean that it is a useless question. In addition, as I already wrote, there are a number of areas in programming in which, in principle, it is impossible to give a definite answer, but these are important areas. The most obvious example is software design. - andreycha
  • five
    @Abyx has repeatedly discussed that since the Russian-language analogues of Server Fault / Super User / Programmers / Code Review are not foreseen, this whole topic is acceptable for ruSO. - andreycha
  • 2
    @andreycha, there is no analogue to cooking.SE either, we will answer the questions "how to cook dumplings"? - Abyx

7 answers 7

The name for the tests do not need to choose beautiful. The name needs to be chosen functional and as short as possible - so that by the minimum number of characters it is clear what he is testing under what conditions, and also to distinguish a specific test from thousands of others.

Common naming patterns are:

  • TestClass_TestMethod_ConditionAndExpectedResult (for unit tests)
  • TestMethod_Condition_ExpectedResult (for unit tests)
  • ConditionAndExpectedResult (for integration tests that test entire blocks of functionality through some entry point)

Moreover, if you have too many different conditions / results in the Condition / ExpectedResult combined under the “AND”, then you need to review either the main goal of the test (what it tests), or the main condition that distinguishes the test from others, and break it on a few separate tests.


First of all, I plead that the title should be informative, but as short as possible. Here, just like with the rest of the code - the smaller the code, the less time it takes to understand it, the easier it is to deal with it. Judging by your name, this is not a simple small unit test, but something more, so you can’t do with a very short name. In your case, if you already have a test class for Customer , I would be limited to the name SelectLastTransactionProcess_CreateRecurringProfile_RedirectUrlShouldContainConfirmation . There are many repetitions of words in your title: transaction, url, recurring profile, etc. They are redundant.

There must be a balance between the length and the information content of the name. It is enough that the names are clear to developers within the project who already have the context and are able to understand what this test is about. And if for this you need to make the length of the name not 30 characters, but 40, do it. But we should not strive to ensure that the names are clear to everyone he meets, while talking about the whole system, since this leads to such kilometer names that are difficult to understand. Look at your regular code, look at the names of the methods. You do not describe in the name of the method every detail, every line? You describe something important, and the rest is hidden inside. Similarly with test methods.


Can anyone else tell what to do with the names of integration tests,> where are 100,500 different conditions?

For example, billing, accepts 5-10 types of price lists as input and, depending on their combinations and combinations of their parameters (also about 5-10), should give different amounts to the output. The container sent to billing takes ~ 30 lines. If you call tests in the style of a post, it will be testPayoutDebtorToGateOntopGateTransactionAmountGateToCreditorFlatPayinDebtorTo‌​GateOntopTotalTransactionAmountGateToCreditorOntopTransactionAmount , but not with full context.

It is necessary to calculate the total amount based on the fact that the price lists could be configured as follows: Commission type of price list of the type of the main transaction - inside / total, commission of 0.1% + 0.5 rubles above, calculation to keep from turnover. The second commission price list ontop / gate .. still in the same style 10 times and a couple of implicit price list dependencies from each other.

I would say that you need a parametric test. This is a test that tests different data sets with the same code. For example, in terms of the xUnit test framework, the parametric test of the Calculator.Add() method might look like this:

 [Theory] // это параметрический тест [InlineDate(1, 2, 3)] // наборы данных, т.е. параметры теста [InlineDate(0, 1, 1)] [InlineDate(-1, 1, 0)] public void Calculator_Add_ShouldReturnCorrectResult(int a, int b, int expectedResult) { var result = Calculator.Add(a, b); Assert.AreEqual(result, expectedResult); } 

With parametric tests, you do not need to list all the conditions in the names - the name contains only the essence of the test, what exactly is being tested, a specific case. In your case, this is something like the РасчетИтоговойСтоимости_ПоОсновнойИДополнительнойСделке on the РасчетИтоговойСтоимости_ПоОсновнойИДополнительнойСделке (translate yourself into English, the area is clearer to you). And your test method may look like this (while in xUnit, test data can be substituted from a property and even from a separate object):

 [Theory] [PropertyData("PriceData")] // тестовые данные находятся в отдельном свойстве public void РасчетИтоговойСтоимости_ПоОсновнойИДополнительнойСделке( ComissionType mainComissionType, Comission mainComission, CalculationType mainCalculationType, ComissionType auxComissionType, Comission auxComission, CalculationType auxCalculationType, double expectedPrice) { // код теста } public static IEnumerable<object[]> PriceData { get { // данные можете захардкодить, а можете прочитать из файла return new[] { new object[] { // основная сделка ComissionType.InsideTotal, new Comission(0.1, 0.5), CalculationType.ОтОборота, // дополнительная сделка ComissionType.OnTopGate, new Comission(0.42, 0.99), CalculationType.ОтБалды, // результат 100500 }, // и так далее для других пар }; } } 

I think in many test frameworks there is the possibility of creating parametric tests. The way of setting the parameters may be different, but the essence remains the same: the data is taken out in the section with the given, only the name of the case remains in the test name.

Additional information on parametric tests can be found on Sergey Teplyakov's blog .

  • I have only two conditions in the test title, and I don’t see a problem with the definition of the test purpose. With the templates you provide, the short name of the test cannot be done in principle. The main question I have is why do you think that it is worth striving for short names, and not informative ones? - Andrey Kolomensky
  • Can you give examples of how you call your tests? - Andrey Kolomensky
  • 2
    @ Onedev.Link to the first question answered in the update. As for the name of the tests: for example, I have a PeriodicTaskExecutor class with the Run method, to which Action is passed, and this method runs once every N seconds. Test names are as follows: Run_MethodWithException_ShouldThrow , Run_ValidMethod_ShouldRunInCycle , Run_MethodWithCancel_ShouldStop . - andreycha

Before the very first "modular test"

Need to negotiate with yourself or the team about what to consider as a “unit test”?

For example, in Roy’s book (see the “Recommended Literature” section) a clear definition of “unit” is given and quite convenient for a programmer.

Without a clear and confident understanding of what a "unit" is, I strongly do not recommend writing unit tests. Even if your understanding of what a unit test is different from what Roy says, it still needs to be unified and stable for YOU or your team. Which requires some agreement.

Understanding "Modular Test" by Roy :

Roy, in my opinion, offers the most convenient and practical naming scheme:

{unit-of-work} _ {scenario} _ {expected-results-or-behavior}

Where: unit-of-work - this is exactly what you mean by 'unit' or as agreed with the team. scenario is a specific action on a test object. expected-results-or-behavior - these are your expectations as to what should happen

Examples:

  1. jsonParse_InvalidFileExtension_ThrowInvalidLogFileException

It is immediately obvious that the parser of the json document is checked on a file with an invalid file extension and therefore an InvalidLogFileException must be thrown.

  1. opimizeByteCode_UnknownInstruction_ReturnMinusOne

Here you can see that when optimizing bytecode, -1 will be returned if an unknown instruction is encountered

Writing unit tests with difficult conditions

  1. This is a completely different question.
  2. In short, in one unit test ONE check. If the condition is difficult, then there are several conditions, and therefore it is necessary to take and break into several checks - unit tests
  3. In any test there should be no conditional logic. If there are two branches in the production code, one if-part, the other else-part. So it is written two modular test.

etc. etc. But it's all a topic for another discussion.

Recommendations on the names of tests of the author

testCustomerRedirectUrlAfterSelectLastTransactionProcessShouldContainsUrlToConfirmRecurringProfilePageWhenTransactionTypeIsCreateRecurringProfile

What is wrong, in my opinion, in your test:

  1. The prefix is ​​'test'. If I watch a test project with a set of unit tests and when I see something, then what is it if not a test? In C # there is [Test] , in python there is py.test . It's enough!
  2. The name is not divided into logical parts. Where is the action? Where is the condition? Where is the expected result? And where are we testing? All mixed up in one pile! Any programmer either is too lazy to read such a name, or will begin to divide into parts and not the fact that he will divide it as the author of the test divided into parts!
  3. The word 'when' is superfluous! If you have an accepted naming scheme, then you or your colleague so know where to look for a condition or an action!

Recommended reading :

  1. I strongly recommend downloading, and it is better to buy the book Art Of Unit Testing. Even if Suddenly do not write in C #. Knowledge is helpful!

  2. Answer SO-Member @Timofey Bondarev to my question. How do I replace the prefix 'test_' with 'should_' in unit tests with the use of unittest?

  • Thank! Art Of Unit Testing already in line, apparently the book is very cool. How do you call tests with difficult conditions? - Andrey Kolomensky
  • test php prefix is ​​required, otherwise it will not be a test - BOPOH
  • @ RAVEN. In the python world, there are similar requirements too, but in the C # world using NUnit, it suffices to apply [Test] - sys_dev
  • Chic answer, on the shelves. - andreycha

1. Test names must be strings

We need to start by saying that the name of the test is the name of the function. Unlike functions, we do not call tests from the code; the name of the test is only needed to describe what it tests once, so that this description is then written to the log with the test results.

Therefore, in some test frameworks, test names are regular strings.
For example in Catch (C ++)

 TEST_CASE("что-то когда такие-то условия") { auto testee = Testee(1, 2); REQUIRE(testee.foo() == 1); } 

Or Mocha (JS and other * script)

 describe('что-то', function () { it('когда такие-то условия', function () { var testee = Testee(1, 2); assert.equal(testee.foo(), 1); }); }); 

An interesting exception is languages ​​where identifiers can have any characters, such as in F #

 [<Test>] let ``twice удваивает числа`` () = twice(1) |> should equal 2 

2. No need to repeat (DRY principle)

If the test framework allows you to group tests, then this should be used. Instead of tests Чтото_КогдаЭто_ВотЭто , Чтото_КогдаДругое_ВотЭто and Чтото_КогдаДругое_ЕщеИЭто tests can be grouped.
Mocha / JS example:

 describe('что-то', function () { var testee = Testee(); describe('когда так-то', function () { it('вот это', function () { testee.setup(1); assert.equal(testee.f(), 1); }); }); describe('когда по другому', function () { testee.setup(2); it('вот это', function () { assert.equal(testee.f(), 20); }); it('и еще это', function () { assert.equal(testee.g(), 21); }); }); }); 

Or for Catch / C ++:

 TEST_CASE("что-то") { auto testee = Testee(); WHEN("когда так-то то вот это") { testee.setup(1); REQUIRE(testee.f() == 1); } WHEN("когда по другому") { testee.setup(2); THEN("вот это") { REQUIRE(testee.f() == 20); } THEN("и еще это") { REQUIRE(testee.g() == 21); } } } 

It also often does not make sense to repeat the test code in the test name. It is necessary to describe what the test checks, and not how it does it. Therefore, instead of StartButton_WhenPressed_BecomesDisabled, you can write

 TEST_F(ButtonsPanelTest, StartButtonPress) { EXPECT_CALL(delegate_mock, Start()); start_button.Press(); ASSERT_FALSE(start_button.IsEnabled()); } 

Typically, the test includes the initial conditions (GIVEN / WHEN part) and the results (THEN part). If for some initial conditions there is one test, then why not write in the title what is checked in the THEN part. This is evident from the code, and if the test fails, the text of the failed check will be written in the log.

Test Name Examples

Test names depend on the test framework and on the test level (unit / integration / regression).

Mocha / JS Regression Tests

 describe("File IO", function() { it("can read text", function() {...}) it("invalid file name error", function() {...}) it("read after EOF error", function() {...}) }) 

Unit tests on Catch / C ++, when Catch uses split test names /

 TEST_CASE_METHOD(ParserFixture, "Notifications/Empty") { SECTION("no 'type' field") {...} SECTION("empty updates") {...} } TEST_CASE_METHOD(ParserFixture, "Notifications/Status update") {...} TEST_CASE_METHOD(ParserFixture, "Notifications/Profile update") {...} 

Newer Catch / C ++ tests tagged

 TEST_CASE("request line", "[http][parser]") {...} TEST_CASE_METHOD(FrameReceiverFixture, "is frame complete", "[websocket]") {...} 

Tests for gtest / C ++ (from Chromium code)

 TEST_F(DownloadItemTest, CompleteDelegate_SetDanger) {...} TEST_F(DownloadItemTest, NotificationAfterOnDownloadTargetDetermined) {...} 

Tests on Golang

 func TestSomeClassMethod_CanDoThis(t *testing.T) {...} 

If the test name cannot be specified as a string, then the simultaneous use of CamelCase and underscore gives a fairly good readability. For example, instead of TEST_CASE("some unit") { WHEN("this state") { THEN("those results") { ... you can write SomeUnit_WhenThisState_ThoseResults .

  • Thank! A useful answer, you need to think about it. There is a question a little off topic, but in the context of your answer it would be appropriate: how do you feel about the rule one test per test ? - Andrey Kolomensky
  • 2
    @ Onedev.Link more correctly is "one group of checks per test". One test should test any one feature (some one aspect of program behavior), and there may be several checks for one feature. But it is desirable not to thrust several unrelated features into one test, except when these features are very small. For example, you can put in 1 test test factorical(0) == 1; factorial(1) == 1; factorial(4) == 24; throws(factorial(-1)) factorical(0) == 1; factorial(1) == 1; factorial(4) == 24; throws(factorial(-1)) factorical(0) == 1; factorial(1) == 1; factorial(4) == 24; throws(factorial(-1)) - there is nothing to worry about all these checks being in the test with the same name. - Abyx

A very long test name can be considered a diagnostic signal. Such signals can be viewed from different points of view, but in any case, as a rule, they indicate the presence or possible occurrence of a problem. The minimum possible options:

  • The test code implements the wrong architecture.
  • This is not a modular, but perhaps an integration test.
  • Incorrect implementation of the actual test module
  • The developer did not get enough sleep and (or) used any transformers of consciousness in excess

Since you gave an example, let's look at it. To begin with, I will try to extract from the English the names of your test Russian meaning:

"Make sure that the URL to which the client is redirected after processing the last transaction should contain the URL corresponding to the return profile page if the transaction type implements the return profile."

Let's assume what the actual body of such a test might look like:

 [Test] public void redirect_to_profile_url_test() { Transaction trn = new TransactionCreateRecurringProfile(); this.executionContext.Add(trn); this.executionContext.ExecuteTransaction(); Assert.AreEqual(this.recurringProfileUrl, this.executionContext.RedirectUrl); } 

In the first approximation, everything seems brief, pretty and readable. Let's suppose what a test might look like that certifies the opposite:

 [Test] public void do_not_redirect_to_profile_url_test() { Transaction trn = new TransactionDoesNotCreateRecurringProfile(); this.executionContext.Add(trn); this.executionContext.ExecuteTransaction(); Assert.AreNotEqual(this.recurringProfileUrl, this.executionContext.RedirectUrl); } 

Naturally, we assume that all the preparatory work before the test is taken from the actual tests:

 [TestFixture] public class RedirectAfterTransactionTest { private ExecutionContext executionContext; private String recurringProfileUrl; [SetUp] public void setUp() { this.executionContext = new RealExecutionContext(); this.recurringProfileUrl = "http://localhost/bla-bla-bla"; } } 

Update

What do I call tests? Something like this (from the working draft):

 public class InMemoryRepositoryTest { @Test public void insert_test() {} @Test public void find_test() {} @Test public void delete_test() {} @Test public void update_data_with_same_id_test() {} @Test public void cant_update_if_item_with_id_not_exists_test() {} @Test public void do_not_insert_other_type_data_test() {} @Test public void insert_with_merge_test() {} @Test public void update_with_merge_test() {} } 

The name of the test class refers to the name of the class being tested in the code. And the names of test methods indicate the names of the tested methods in the test class and try to briefly report on the testing conditions of each specific method.

I prefer not to get too carried away with the details in the names of test methods, because as the code refactor, the name of the method can get out of sync with the content. This is especially true when the project is being made up under strong temporary pressure from the customer.

Naming rules are some kind of Tao or Kung Fu ... Balance must be maintained. If the name of the test method is longer than the code itself contained in the test method, then the developer who refactor the method may be easier and faster to read and understand the code than to wade through the literary twists and turns of the name in English.

  • What to do if the functional means not only a boolean state? Under different scenarios and conditions, my url will have different meanings. Получается что всё-таки надо выносить в название теста уточнения?Andrey Kolomensky
  • Можете привести примеры как вы называете свои тесты?Andrey Kolomensky
  • one
    Примеры имен моих тестов добавил в запись выше.SergeyEgorov

Думаю, тестовые методы тут не стоит выделять отдельно - в целом они подчиняются тем же правилам именования, что и обычные методы. В числе этих правил - не давать методу слишком длинное и неудобочитаемое название. Краткость - сестра таланта, как писал один русский писатель. Для всеобъемлющего описания того, что именно тестирует метод, человечество изобрело комментарии, в которых можно растекаться мыслью по древу (хотя злоупотреблять не стоит и там).

Ваш же вариант именования сильно похож на применявшуюся несколько столетий назад витиеватую манеру давать названия книгам, когда в названии описывается едва ли не весь сюжет книги. Например, полное название Робинзона Крузо звучит устрашающе-длинно:

"Жизнь, необыкновенные и удивительные приключения Робинзона Крузо, моряка из Йорка, прожившего 28 лет в полном одиночестве на необитаемом острове у берегов Америки близ устьев реки Ориноко, куда он был выброшен кораблекрушением, во время которого весь экипаж корабля кроме него погиб, с изложением его неожиданного освобождения пиратами; написанные им самим"

  • Как вы предложите называть тесты для различных условий? К примеру, тест проверяющий какой редирект урл у первого процесса определенного типа транзакции?Andrey Kolomensky
  • Можете привести примеры как вы называете свои тесты?Andrey Kolomensky
  • @Onedev.Link да как угодно. Для описания у вас есть комментарииDreamChild
  • one
    Вы, конечно, в курсе основного минуса комментариев? Они устаревают. Как они мне помогут когда тест обвалится? Это нормально лезть в исходный код теста для определение того что сломалось? Почему вы используете комментарии, вместо жестко прописанных условий в названии теста?Andrey Kolomensky
  • one
    @Onedev.Link вы занимаетесь перфекционизмом в худшем его проявлении. Лезть в тесты для определения поломки рано или поздно придется, и чего-то прям уж ужасного в этом нет (в конце концов скорее всего вы не напишете идеальный все покрывающий тест сразу), а устареть у вас может и названием метода. Даже если вам настолько принципиально описание логики теста именно в его названии (сомнительная необходимость), и при этом названия получаются чудовищно длинными, то вам стоит подумать о разбиениии вашего тестового класса на несколько новых, каждый из которых будет тестировать что-то более конкретноеDreamChild

Я считаю что название модульного теста должно быть ёмким. Особенно если тест пишется в BDD стиле. Особенно если ваш фреймворк для тестирования умеет это раскладывать в читабельные отчёты.

Нет никакой разницы, насколько длинное имя метода с точки зрения читающего тест программиста. Есть только один важный аспект, где длина метода будет пропорциональна пониманию, что он тестирует - это отчёт, который будет читать другой человек, не программист.

На мой взгляд ёмкие названия (пример):

  • testShouldAlwaysPreparePathsAndConvertThemInToArray
  • testShouldRegisterNamespacesForMultipleDirectories
  • testShouldThrowExceptionIfDbIsMissingOrInvalid
  • testShouldSetOptionsWhenConfigPassedToTheConstructor

В любом приличном фреймворке для тестирования такие имена методов трансформируются в что-то подобное (пример):

 Test should always prepare paths and convert them in to array [OK] Test should register namespaces for multiple directories [OK] Test should throw exception if db is missing or invalid [OK] Test should set option whet config passed to the constructor [FAIL] 

Согласитесь, такое гораздо приятнее читать. Это можно показать кому угодно, ПМ например или фронтенд разработчику : ) и ему совсем не обязательно знать фреймворк, который вы используете или язык.

Не останавливайтесь на длине метода. Делайте его на столько длинным, насколько этого требует сценарий. Да, конечно, в пределах разумного. Несколько не с руки будет читать такой тест, если он из-за ширины экрана, в лучшем случае разобъётся на несколько строк, а то и вовсе обрежется.

  • слово test в каждом названии ничуть не прибавляет емкости и смысла. И разве это Test should always prepare paths? Нет, тестируемый объект должен.Nick Volynkin
  • I agree. But the question was not exactly which words to use, but how long. Though I should ...:) - serghei
  • one
    in this sense, I like how tests are written on scala. There, the test object should bla-bla. - Nick Volynkin

I was taught that a unit test always has a name pattern: when

whenThen

 @Test whenStringArgInThenArgAddInArrayList() { Item item = new Item(); item.getList.add("a"); assertThat(item.getList[0].get, is("a")); }