单元测试比开发更耗时_进行更好的单元测试,第2部分命名单元测试

单元测试比开发更耗时

文章(ARTICLE)

This article focuses on naming unit tests. While quite a few competing pieces of advice can be found on this topic, most of them don’t do a good enough job of improving your unit tests. Here, I will endeavor to describe some of these naming practices and demonstrate why they’re usually not the best choice. I’ll also provide you with an alternative — a simple set of guidelines for naming tests in a way that makes them readable to not only the programmer who wrote them but to any other person familiar with the problem domain.

本文重点介绍单元测试的命名。 尽管可以在此主题上找到许多相互竞争的建议,但大多数建议在改善单元测试方面做得不够好。 在这里,我将尽力描述其中的一些命名惯例,并说明为什么它们通常不是最佳选择。 我还将为您提供另一种选择-一套简单的准则来命名测试,以某种方式使测试不仅对编写测试的程序员而且对熟悉问题域的任何其他人员可读。

Check out part 1 of this series here.

在这里查看本系列的第1部分。

命名单元测试 (Naming a unit test)

How should you name a unit test? I’ve seen and tried a lot of naming conventions over the past decade. One of the most prominent (and probably one of the least helpful) is the following convention:

您应该如何命名单元测试? 在过去的十年中,我已经看到并尝试了很多命名约定。 以下约定是最突出的(也许是最有用的之一):

[MethodUnderTest]_[Scenario]_[ExpectedResult] where:

[MethodUnderTest]_[Scenario]_[ExpectedResult] ,其中:

  • MethodUnderTest is the name of the method you’re testing.

    MethodUnderTest是您要测试的方法的名称。

  • Scenario is the condition under which you test the method.

    Scenario是测试方法的条件。

  • ExpectedResult is what you expect the method under test to do in the current scenario.

    ExpectedResult是您希望被测试方法在当前场景下所执行的操作。

It’s unhelpful specifically because it encourages you to focus on implementation details instead of the behavior. On the contrary, simple phrases in plain English do a much better job as they’re more expressive and don’t box you into a rigid naming structure. They allow you to describe the system behavior in a way that is meaningful to a customer or a domain expert. To give you an example of a test entitled in plain English, here’s the test from listing four once again:

这样做特别无济于事,因为它鼓励您专注于实现细节而不是行为。 相反,简单的英语短语表现得更好,而且不会使您陷入僵硬的命名结构中,因此效果更好。 它们使您能够以对客户或领域专家有意义的方式描述系统行为。 为了给您提供以简单英语标题进行的测试的示例,这是再次列出四个测试的测试:

public class CalculatorTests
{
[Fact]
public void Sum_of_two_numbers()
{
double first = 10;
double second = 20;
var sut = new Calculator();

double result = sut.Sum(first, second);

Assert.Equal(30, result);
}
}

How could the test’s name be re-written using the [MethodUnderTest]_[Scenario]_[ExpectedResult] convention? Probably something like this:

如何使用[MethodUnderTest]_[Scenario]_[ExpectedResult]约定重写测试的名称? 大概是这样的:

public void Sum_TwoNumbers_ReturnsSum()

This is because the method under test is Sum, the scenario includes two numbers, and the expected result is a sum of those two numbers. The new name looks logical to a programmer’s eye, but does it help the test readability? Not at all. The latter version is all Greek to an uninformed person. Think about it: why are there two Sum words in the name of the test? And what’s this Returns phrasing all about? Where’s the sum returned to? No one but the person who wrote it will have any idea what’s going on here.

这是因为被测方法是Sum ,方案包含两个数字,并且预期结果是这两个数字的总和。 新名称在程序员看来很合逻辑,但这是否有助于测试的可读性? 一点也不。 对于不知情的人,后一种版本全为希腊文。 考虑一下:为什么测试名称中有两个Sum单词? 这是什么Returns表述? 返还到哪里? 除了写这本书的人,没人会知道这里发生了什么。

You might think that it doesn’t matter what a non-programmer thinks of this name. After all, unit tests are written by programmers, not domain experts. And programmers are good at deciphering cryptic names, it’s their job!

您可能会认为非程序员对该名称的看法并不重要。 毕竟,单元测试是由程序员而不是领域专家编写的。 程序员擅长破译加密名称,这是他们的工作!

This is true but only to a certain degree. Cryptic names impose a cognitive tax on everyone — programmer or not. They require additional brain capacity to figure out what exactly the test verifies and how it relates to business requirements. It may not seem like much but the mental burden adds up over time. It slowly but surely increases the maintenance cost for the entire test suite. It’s noticeable, when you return to the test, if you forgot about the feature’s specifics, or try to understand a test written by a colleague. Reading someone else’s code is already difficult enough so any help that you can provide in understanding it is of much use.

这是正确的,但仅在一定程度上是正确的。 隐名对所有人(无论是否为程序员)都征收认知税。 他们需要额外的大脑能力,才能弄清楚该测试可以验证的内容以及它与业务需求的关系。 看起来似乎不多,但精神负担会随着时间而加重。 它会缓慢但肯定会增加整个测试套件的维护成本。 当您返回测试时,如果您忘记了该功能的详细信息,或者试图了解一位同事编写的测试,这将很明显。 读取他人的代码已经非常困难,因此您可以提供的帮助您理解它的任何帮助。

Here are the two versions again:

这又是两个版本:

public void Sum_of_two_numbers()
public void Sum_TwoNumbers_ReturnsSum()

The initial name written in plain English is much simpler to read. It is a down-to-earth description of the behavior under test.

用简单的英语写的名字更容易阅读。 这是对被测行为的详尽描述。

单元测试命名准则 (Unit test naming guidelines)

Here are some basic rules for naming tests:

以下是命名测试的一些基本规则:

  • No rigid naming policy. You can’t fit a high-level description of a complex behavior into a narrow box of such a policy. Allow freedom of expression.

    没有严格的命名政策。 您不能将对复杂行为的高级描述放在这种策略的狭窄框中。 允许言论自由。
  • Name the test as if you were describing the scenario to a non-programmer person who is familiar with the problem domain. A domain expert or a business analyst are good examples.

    将测试命名为好像是向熟悉问题域的非程序员描述场景一样。 领域专家或业务分析师就是很好的例子。
  • Separate words by underscores. It helps improve readability of long names.

    用下划线将单词分开。 它有助于提高长名称的可读性。

Notice that I didn’t use underscores when naming the test class, CalculatorTests. Classes’ names are normally not long and they read fine without underscores.

注意,在命名测试类CalculatorTests时,我没有使用下划线。 班级的名称通常不长,没有下划线的情况下,它们可以很好地阅读。

Also notice that, although I use the pattern [ClassName]Tests when naming test classes, it doesn’t mean that the tests are limited to verifying only that ClassName. Remember, the unit in unit testing is a unit of behavior, not a class. This unit can span across one or several classes, the size is irrelevant. Still, you have to start somewhere. View the class in [ClassName]Tests as that: an entry point — an API — using which you can verify a unit of behavior.

还要注意,尽管在命名测试类时使用[ClassName]Tests模式,但这并不意味着测试仅限于仅验证那个ClassName 。 请记住,在单元测试单位行为的单位,不是一类。 这个单元可以跨越一个或几个类,大小无关紧要。 不过,您必须从某个地方开始。 在[ClassName]Tests查看类,如下所示:入口点[ClassName]Tests您可以使用它来验证行为单位。

示例:将测试重命名为指南 (Example: renaming a test towards the guidelines)

Let’s now take a test as an example and try to gradually improve its name using the guidelines above. In listing 10, you can see a test verifying that a delivery with a past date is invalid. The test's name is written using the rigid naming policy that doesn’t help with the test readability.

现在以测试为例,并尝试使用上述指南逐步提高其名称。 在清单10中,您可以看到一个验证过去日期的交货无效的测试。 测试的名称是使用严格的命名策略编写的,该策略不利于测试的可读性。

Listing 10. A test named using the rigid naming policy.

清单10.使用严格命名策略命名的测试。

[Fact]
public void IsDeliveryValid_InvalidDate_ReturnsFalse()
{
DeliveryService sut = new DeliveryService();
DateTime pastDate = DateTime.Now.AddDays(-1);
Delivery delivery = new Delivery
{
Date = pastDate
};

bool isValid = sut.IsDeliveryValid(delivery);

Assert.False(isValid);
}

This test checks that DeliveryService properly identifies a delivery with an incorrect date as invalid. How would you re-write the test’s name in plain English? The following is a good first try:

此测试检查DeliveryService是否正确地将日期不正确的DeliveryService标识为无效。 您将如何用普通的英语来重写测试的名称? 以下是一个不错的第一次尝试:

public void Delivery_with_invalid_date_should_be_considered_invalid()

Notice two things in the new version:

请注意新版本中的两件事:

  • The name now makes sense to a non-programmer. This means that programmers have an easier time understanding it too.

    现在,该名称对于非程序员来说很有意义。 这意味着程序员也可以更轻松地了解它。
  • The name of the SUT’s method — IsDeliveryValid — is no longer part of the test’s name.

    SUT方法的名称IsDeliveryValid不再是测试名称的一部分。

The second point is a natural consequence of rewriting the test’s name in plain English and can be easily overlooked. This consequence is important and can be elevated into a guideline of its own.

第二点是用简单的英语重写测试名称的自然结果,并且很容易被忽略。 这一结果很重要,可以提升为自己的准则。

Image for post
Don’t include the name of the SUT’s method in the test’s name.
不要在测试名称中包含SUT方法的名称。

Remember, you don’t test code, rather you test application behavior. Therefore, the name of the method used in the test doesn’t matter. As I mentioned previously, the SUT is an entry point, a means to invoke a behavior. You can decide to rename the method under test to IsDeliveryCorrect and it has no effect on the SUT’s behavior. Still, if you follow the original naming convention, you need to rename the test. Once again, this shows that targeting code instead of behavior couples tests to that code’s implementation details which negatively affects the test suite maintainability. The only exception to this guideline is when you work on utility code. Such code doesn’t contain business logic — its behavior doesn’t go much beyond simple auxiliary functionality and doesn’t mean anything to the business people. It’s fine to use the SUT’s method names there.

请记住,您不是在测试代码,而是在测试应用程序的行为。 因此,测试中使用的方法的名称无关紧要。 如前所述,SUT是一个入口点,是一种调用行为的方式。 您可以决定将被测方法重命名为IsDeliveryCorrect ,它对SUT的行为没有影响。 但是,如果您遵循原始的命名约定,则需要重命名测试。 这再次表明,目标代码而不是行为会将测试与该代码的实现详细信息耦合,从而对测试套件的可维护性产生负面影响。 该准则的唯一例外是您使用实用程序代码时。 这样的代码不包含业务逻辑-它的行为并没有超出简单的辅助功能,并且对业务人员没有任何意义。 在那里使用SUT的方法名称是可以的。

Let’s get back to our example. The new version of the test’s name is a good start but it can be improved further. What does it mean for a delivery date to be invalid, exactly? From the test in listing 10 we can see that an invalid date is any date in the past. This makes sense — you should only be allowed to choose a delivery date in the future.

让我们回到我们的例子。 新版本的测试名称是一个不错的开始,但可以进一步改进。 交货日期无效意味着什么? 从清单10中的测试中,我们可以看到无效日期是过去的任何日期。 这很有道理-应该只允许您选择将来的交货日期。

Let’s be specific and reflect this knowledge in the test’s name:

让我们具体说明一下,并以测试的名称反映这些知识:

public void Delivery_with_past_date_should_be_considered_invalid()

This is better, but still not ideal. It’s too verbose. We can safely get rid of the word considered without any loss of meaning:

这更好,但仍然不理想。 太冗长了。 我们可以放心地删除所considered的单词,而不会失去任何含义:

public void Delivery_with_past_date_should_be_invalid()

The wording should be is another common anti-pattern; a test is a single, atomic fact about a unit of behavior. When stating a fact there’s no place for a wish or a desire. Name the test accordingly.

措辞应该是另一种常见的反模式; 测试是关于行为单位的单个原子事实。 陈述事实时,没有希望或欲望的地方。 相应地命名测试。

Replace should be with is:

替换

public void Delivery_with_past_date_is_invalid()

Finally, there’s no need to avoid basic English grammar. Articles help the test read flawlessly. Add an indefinite article (“a”) to the test’s name:

最后,无需避免基本的英语语法。 文章可帮助测试无误阅读。 在测试名称中添加不定冠词(“ a”):

public void Delivery_with_a_past_date_is_invalid()

There you go. This final version is a straight-to-the-point statement of a fact which describes one of the facets of the application behavior under test. In this particular case — determining whether a delivery can be done.

妳去最终版本是对事实的直截了当的陈述,它描述了受测应用程序行为的各个方面。 在这种特殊情况下-确定是否可以交货。

重构到参数化测试 (Refactoring to parameterized tests)

One test is usually not enough to fully describe a unit of behavior. Such a unit normally consists of multiple components, each of which should be captured with its own test. If the behavior is complex enough, the number of tests describing it can grow dramatically and may become unmanageable. Luckily, most unit testing frameworks provide functionality which allows you to group similar tests (see figure 2).

一个测试通常不足以完全描述一个行为单位。 这种单元通常由多个组件组成,每个组件都应通过自己的测试来捕获。 如果行为足够复杂,则描述该行为的测试数量可能会急剧增加,并且可能变得难以管理。 幸运的是,大多数单元测试框架都提供了使您可以对相似测试进行分组的功能(参见图2)。

Image for post
Figure 2. A typical application exhibits multiple behaviors. The higher the complexity of the behavior, the more facts it takes to fully describe. Each fact is represented by a test. Similar facts can be grouped into a single test method. 图2.一个典型的应用程序表现出多种行为。 行为的复杂性越高,就需要更多地描述事实。 每个事实都由一个测试表示。 可以将相似的事实归为一个测试方法。

Let’s say that our delivery functionality works in such a way that the soonest allowed delivery date is two days from now. Clearly, the one test we currently have is not enough. In addition to the test checking a past delivery date, we’ll also need tests that check for today’s date, tomorrow’s date, and the date after that.

假设我们的交付功能以这样的方式工作,即允许的最快交付日期是从现在起两天。 显然,我们目前进行的一项测试还不够。 除了检查过去的交货日期的测试外,我们还需要检查今天的日期,明天的日期以及之后的日期的测试。

The existing test is called Delivery_with_a_past_date_is_invalid. We could add three more:

现有测试称为Delivery_with_a_past_date_is_invalid 。 我们可以再添加三个:

public void Delivery_for_today_is_invalid()
public void Delivery_for_tomorrow_is_invalid()
public void The_soonest_delivery_date_is_two_days_from_now()

This results in four test methods, with the only difference between them being the delivery date.

这样就产生了四种测试方法,它们之间的唯一区别是交货日期。

A better approach is to group these tests into one in order to reduce the amount of test code. xUnit (and most of the other test frameworks) has a feature called parameterized tests which allows you to do exactly that. Listing 11 shows how such grouping looks. Each InlineData attribute represents a separate fact about the system, it’s a test case in its own right.

更好的方法是将这些测试组合在一起,以减少测试代码的数量。 xUnit(以及大多数其他测试框架)具有称为参数化测试的功能,使您可以准确地做到这一点。 清单11显示了这种分组的外观。 每个InlineData属性代表一个有关系统的独立事实,它本身就是一个测试用例。

Listing 11. A test that encompasses several facts.

清单11.包含几个事实的测试

public class DeliveryServiceTests
{
[InlineData(-1, false)] (1)
[InlineData(0, false)] (1)
[InlineData(1, false)] (1)
[InlineData(2, true)] (1)
[Theory]
public void Can_detect_an_invalid_delivery_date(
int daysFromNow, (2)
bool expected) (2)
{
DeliveryService sut = new DeliveryService();
DateTime deliveryDate = DateTime.Now
.AddDays(daysFromNow); (3)
Delivery delivery = new Delivery
{
Date = deliveryDate
};

bool isValid = sut.IsDeliveryValid(delivery);

Assert.Equal(expected, isValid); (3)
}
}
  1. The InlineData attribute sends a set of input values to the test method. Each line represents a separate fact about the behavior.

    InlineData属性将一组输入值发送到测试方法。 每行代表有关行为的单独事实。

  2. Parameters to which the attributes attach the input values

    属性附加输入值的参数

  3. Uses the parameters

    使用参数

Notice the use of the [Theory] attribute instead of [Fact]. A theory is a bunch of facts about the behavior. Each fact is now represented by an [InlineData] line rather than a separate test. I also renamed the test method into something more generic. It doesn’t mention what constitutes a valid or invalid date anymore.

注意使用[Theory]属性而不是[Fact] 。 理论是关于行为的一堆事实。 现在,每个事实都由[InlineData]行而不是单独的测试表示。 我还将测试方法重命名为更通用的名称。 它不再提及什么构成有效日期或无效日期。

Note that I didn’t name the test Detects_an_invalid_delivery_date, although this version is shorter. This is because DeliveryService doesn’t always detect an invalid delivery date; only when it’s invalid. Hence the name Can_detect_an_invalid_delivery_date— it may or may not detect it, depending on what date we specify for the delivery.

请注意,尽管此版本较短,但我没有将测试命名为Detects_an_invalid_delivery_date 。 这是因为DeliveryService并不总是检测到无效的交货日期,而是因为DeliveryService日期不正确。 仅在无效时。 因此,名称为Can_detect_an_invalid_delivery_date它可能会或可能不会检测到它,具体取决于我们为交货指定的日期。

Parameterized tests allow you to significantly reduce the amount of test code but this benefit comes at a cost. It’s now hard to figure out what facts the test method represents. And the more parameters there are, the harder it becomes. As a compromise, you can extract the positive test case into its own test and benefit from the descriptive naming where it matters the most — in determining what differentiates valid and invalid delivery dates (listing 12).

参数化测试使您可以显着减少测试代码的数量,但这是有代价的。 现在很难弄清楚测试方法代表什么事实。 而且参数越多,难度就越大。 作为一种折衷,您可以将肯定的测试用例提取到其自己的测试中,并从最重要的描述性命名中受益-确定哪些区分有效和无效的交付日期(清单12)。

Listing 12. Two tests verifying the positive and negative scenarios.

清单12.验证正面和负面场景的两个测试。

public class DeliveryServiceTests
{
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
[Theory]
public void Detects_an_invalid_delivery_date(int daysFromNow)
{
/* ... */
}

[Fact]
public void The_soonest_delivery_date_is_two_days_from_now()
{
/* ... */
}
}

It also simplifies the negative test cases because you can remove the expected boolean parameter from the test method. You can transform the positive test method into a parameterized test as well, to test multiple dates. As you can see, there is a trade-off between the amount of test code and the readability of that code. As a rule of thumb, keep both positive and negative test cases together in a single method only when it’s self-evident from the input parameters which case stands for what. Extract the positive test cases otherwise. And if the behavior is too complicated, don’t use the parameterized tests at all.

它还可以简化否定的测试用例,因为您可以从测试方法中删除expected布尔参数。 您也可以将肯定测试方法转换为参数化测试,以测试多个日期。 如您所见,测试代码的数量和该代码的可读性之间存在一个权衡。 根据经验,只有从输入参数不言而喻的情况下,才能将阳性和阴性测试用例放在一个方法中。 否则提取阳性测试用例。 如果行为太复杂,则根本不要使用参数化测试。

Represent each negative and positive test case with its own test method.

用自己的测试方法代表每个阴性和阳性测试用例。

为参数化测试生成数据 (Generating data for parameterized tests)

Notice that in listing 11, I used the daysFromNow parameter as an input to the test method. Why not the date and time, you might ask? Unfortunately, this code won’t work (listing 13).

注意,在清单11中,我使用daysFromNow参数作为测试方法的输入。 您可能会问,为什么不选择日期和时间呢? 不幸的是,此代码无法正常工作(清单13)。

Listing 13. Calls to runtime don’t work in attributes.

清单13.调用运行时在属性中不起作用。

[InlineData(DateTime.Now.AddDays(-1), false)]
[InlineData(DateTime.Now, false)]
[InlineData(DateTime.Now.AddDays(1), false)]
[InlineData(DateTime.Now.AddDays(2), true)]
[Theory]
public void Can_detect_an_invalid_delivery_date(
DateTime deliveryDate,
bool expected)
{
DeliveryService sut = new DeliveryService();
Delivery delivery = new Delivery
{
Date = deliveryDate
};

bool isValid = sut.IsDeliveryValid(delivery);

Assert.Equal(expected, isValid);
}

In C#, the content of all attributes is evaluated at compile-time, and you have to use only those values which the compiler can understand: constants, literals, and typeof() expressions. The call to DateTime.Now relies on the .NET runtime and isn’t allowed. A way to overcome this problem can be found with xUnit, which has a feature to generate custom data to feed into the test method: [MemberData]. Listing 14 shows how we can re-write the above test using this feature.

在C#中,所有属性的内容都是在编译时评估的,您只需要使用编译器可以理解的那些值:常量,文字和typeof()表达式。 对DateTime.Now的调用依赖于.NET运行时,因此是不允许的。 可以通过xUnit找到解决此问题的方法,该功能具有生成定制数据以馈入测试方法的功能: [MemberData] 。 清单14显示了如何使用此功能重写上面的测试。

Listing 14. Generating complex data for the parameterized test.

清单14.生成用于参数化测试的复杂数据。

[Theory]
[MemberData(nameof(Data))]
public void Can_detect_an_invalid_delivery_date(
DateTime deliveryDate,
bool expected)
{
/* ... */
}

public static List<object[]> Data()
{
return new List<object[]>
{
new object[] { DateTime.Now.AddDays(-1), false },
new object[] { DateTime.Now, false },
new object[] { DateTime.Now.AddDays(1), false },
new object[] { DateTime.Now.AddDays(2), true }
};
}

MemberData accepts the name of a static method which generates a collection of input data (the compiler translates nameof(Data) into a "Data" literal). Each element of the collection is a collection which gets mapped into the two input parameters: deliveryDate and expected. This feature allows you to overcome the compiler’s restrictions and use parameters of any type in the parameterized tests.

MemberData接受生成输入数据集合的静态方法的名称(编译器将nameof(Data)转换为"Data"文字)。 集合的每个元素都是一个集合,该集合被映射到两个输入参数: deliveryDateexpected 。 此功能使您可以克服编译器的限制,并在参数化测试中使用任何类型的参数。

使用断言库进一步提高测试的可读性 (Using an assertion library to further improve test readability)

One more thing that you can do to improve test readability is to use an assertion library. I personally prefer Fluent Assertions (https://fluentassertions.com/) but there are several competing libraries in .NET in this area. Shouldly is a good example (https://github.com/shouldly/shouldly).

要提高测试的可读性,您可以做的另一件事是使用断言库。 我个人更喜欢Fluent断言( https://fluentassertions.com/ ),但是.NET在此领域有一些竞争的库。 应该是一个很好的例子( https://github.com/shouldly/shouldly )。

The main benefit is how, with an assertion library, you can restructure the assertions to look more readable. Here’s one of our earlier tests:

主要的好处是使用断言库可以如何重组断言以使其更具可读性。 这是我们较早的测试之一:

[Fact]
public void Sum_of_two_numbers()
{
var sut = new Calculator();

double result = sut.Sum(10, 20);

Assert.Equal(30, result);
}

Now compare it to the following:

现在将其与以下内容进行比较:

[Fact]
public void Sum_of_two_numbers()
{
var sut = new Calculator();

double result = sut.Sum(10, 20);

result.Should().Be(30);
}

The assertion from the second test reads like plain English, which is exactly how you want all your code to read. What is important here is word order: we prefer to absorb information in the form of stories. All stories adhere to the specific pattern:

第二个测试的断言读起来像普通的英语,这正是您希望所有代码都阅读的方式。 这里重要的是词序:我们更喜欢以故事的形式吸收信息。 所有故事都遵循特定的模式:

[Subject] [action] [object].

For example:

例如:

Bob opened the door.

Here, Bob is a subject, opened is an action and the door is an object. The same rule can be applied to code. result.Should().Be(30) reads better than Assert.Equal(30, result) precisely because it follows the story pattern. It’s a simple story in which result is a subject, should be is an action, and 30 is an object.

在这里, Bob是一个主体, opened是一个动作, the door是一个物体。 相同的规则可以应用于代码。 result.Should().Be(30)阅读要比Assert.Equal(30, result)更好Assert.Equal(30, result)因为它遵循故事模式。 这是一个简单的故事,其中result是一个主题, should be是一个动作,而30是一个对象。

结论 (Conclusion)

And there you have it, improving you unit tests through better naming practices. As a side note, the paradigm of Object-Oriented Programming (OOP) has come to success partly because of this readability benefit. It too allows you to structure the code in a way that reads like a story. The Fluent Assertions library also provides numerous helper methods to assert against numbers, strings, collections, dates and time, and much more. The drawback is that such a library is an additional dependency which you might not want to introduce to your project.

有了它,就可以通过更好的命名实践来改进单元测试。 附带说明一下,面向对象编程(OOP)的范例之所以成功,部分原因是它具有可读性。 它也允许您以类似于故事的方式来构造代码。 Fluent Assertions库还提供了许多辅助方法,可以针对数字,字符串,集合,日期和时间等进行断言。 缺点是,此类库是您可能不想引入到项目中的附加依赖项。

That’s all for this article. If you want to learn more about the book, check it out on liveBook here.

这就是本文的全部内容。 如果您想了解更多有关这本书的信息,请在liveBook上查看

This article was originally published here: https://freecontent.manning.com/making-better-unit-tests/

这篇文章最初发表在这里: https : //freecontent.manning.com/making-better-unit-tests/

翻译自: https://medium.com/@ManningBooks/making-better-unit-tests-part-2-naming-unit-tests-8a7279c3ea4

单元测试比开发更耗时

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值