内容简介:在软件工程领域,无论对于前端或是后端工程师都必须对自己的代码质量负责,尤其是后端工程师,高度的单元测试覆盖率是最有效的手段之一。以Java代码为例,有着Junit、Mockito等开源单元测试框架,并且非常便于测试集成。Golang自身便带有单元测试模块,都是为提高软件质量而设计的,优秀的开源框架其单元测试覆盖率都非常高,才能为其软件质量提供保障。一名优秀的后端工程师必须具备编写单元测试的习惯,而且测试覆盖率越高你的软件质量也就越好,其次高度覆盖的测试用例方便软件的迭代与维护,能够有助于后者快速理解代码与定
image
在软件工程领域,无论对于前端或是后端工程师都必须对自己的代码质量负责,尤其是后端工程师,高度的单元测试覆盖率是最有效的手段之一。
以 Java 代码为例,有着Junit、Mockito等开源单元测试框架,并且非常便于测试集成。Golang自身便带有单元测试模块,都是为提高软件质量而设计的,优秀的开源框架其单元测试覆盖率都非常高,才能为其软件质量提供保障。
一名优秀的后端工程师必须具备编写单元测试的习惯,而且测试覆盖率越高你的软件质量也就越好,其次高度覆盖的测试用例方便软件的迭代与维护,能够有助于后者快速理解代码与定位问题。
高覆盖体现在测试用例覆盖public方法、函数中的if-else等逻辑、参数检查、内部定义等。
这里以Junit和Google的Truth为案例展示一个测试用例案例。
定义一个Resource实体类型,实体内部定义了参数必要检查、public函数:
/** * {@link Resource} represents a resource, which capture identifying information about the entities * for which signals (stats or traces) are reported. */ @Immutable @AutoValue public abstract class Resource { private static final int MAX_LENGTH = 255; private static final String ERROR_MESSAGE_INVALID_CHARS = " should be a ASCII string with a length greater than 0 and not exceed " + MAX_LENGTH + " characters."; private static final String ERROR_MESSAGE_INVALID_VALUE = " should be a ASCII string with a length not exceed " + MAX_LENGTH + " characters."; private static final Resource EMPTY = new AutoValue_Resource(Collections.emptyMap()); Resource() {} public static Resource getEmpty() { return EMPTY; } /** * Returns a map of labels that describe the resource. * * @return a map of labels. */ public abstract Map<String, String> getLabels(); /** * Returns a {@link Resource}. * * @param labels a map of labels that describe the resource. * @return a {@code Resource}. * @throws NullPointerException if {@code labels} is null. * @throws IllegalArgumentException if label key or label value is not a valid printable ASCII * string or exceed {@link #MAX_LENGTH} characters. */ public static Resource create(Map<String, String> labels) { checkLabels(labels); return new AutoValue_Resource(labels); } /** * Returns a new, merged {@link Resource} by merging the current {@code Resource} with the * {@code other} {@code Resource}. In case of a collision, current {@code Resource} takes * precedence. * * @param other the {@code Resource} that will be merged with {@code this}. * @return the newly merged {@code Resource}. */ public Resource merge(@Nullable Resource other) { if (other == null) { return this; } Map<String, String> mergedLabelMap = new LinkedHashMap<>(other.getLabels()); // Labels from resource overwrite labels from otherResource. for (Map.Entry<String, String> entry : this.getLabels().entrySet()) { mergedLabelMap.put(entry.getKey(), entry.getValue()); } return new AutoValue_Resource(mergedLabelMap); } private static void checkLabels(Map<String, String> labels) { for (Map.Entry<String, String> entry : labels.entrySet()) { Utils.checkArgument( isValidAndNotEmpty(entry.getKey()), "Label key" + ERROR_MESSAGE_INVALID_CHARS); Utils.checkArgument( isValid(entry.getValue()), "Label value" + ERROR_MESSAGE_INVALID_VALUE); } } /** * Determines whether the given {@code String} is a valid printable ASCII string with a length not * exceed {@link #MAX_LENGTH} characters. * * @param name the name to be validated. * @return whether the name is valid. */ private static boolean isValid(String name) { return name.length() <= MAX_LENGTH && StringUtils.isPrintableString(name); } /** * Determines whether the given {@code String} is a valid printable ASCII string with a length * greater than 0 and not exceed {@link #MAX_LENGTH} characters. * * @param name the name to be validated. * @return whether the name is valid. */ private static boolean isValidAndNotEmpty(String name) { return !name.isEmpty() && isValid(name); } }
为其提供高度覆盖的测试用例:
/** Unit tests for {@link Resource}. */ @RunWith(JUnit4.class) public final class ResourceTest { @Rule public final ExpectedException thrown = ExpectedException.none(); private static final Resource DEFAULT_RESOURCE = Resource.create(Collections.emptyMap()); private Resource resource1; private Resource resource2; @Before public void setUp() { Map<String, String> labelMap1 = new HashMap<>(); labelMap1.put("a", "1"); labelMap1.put("b", "2"); Map<String, String> labelMap2 = new HashMap<>(); labelMap2.put("a", "1"); labelMap2.put("b", "3"); labelMap2.put("c", "4"); resource1 = Resource.create(labelMap1); resource2 = Resource.create(labelMap2); } @Test public void create() { Map<String, String> labelMap = new HashMap<>(); labelMap.put("a", "1"); labelMap.put("b", "2"); Resource resource = Resource.create(labelMap); assertThat(resource.getLabels()).isNotNull(); assertThat(resource.getLabels().size()).isEqualTo(2); assertThat(resource.getLabels()).isEqualTo(labelMap); Resource resource1 = Resource.create(Collections.emptyMap()); assertThat(resource1.getLabels()).isNotNull(); assertThat(resource1.getLabels()).isEmpty(); } @Test public void testResourceEquals() { Map<String, String> labelMap1 = new HashMap<>(); labelMap1.put("a", "1"); labelMap1.put("b", "2"); Map<String, String> labelMap2 = new HashMap<>(); labelMap2.put("a", "1"); labelMap2.put("b", "3"); labelMap2.put("c", "4"); new EqualsTester() .addEqualityGroup(Resource.create(labelMap1), Resource.create(labelMap1)) .addEqualityGroup(Resource.create(labelMap2)) .testEquals(); } @Test public void testMergeResources() { Map<String, String> expectedLabelMap = new HashMap<>(); expectedLabelMap.put("a", "1"); expectedLabelMap.put("b", "2"); expectedLabelMap.put("c", "4"); Resource resource = DEFAULT_RESOURCE.merge(resource1).merge(resource2); assertThat(resource.getLabels()).isEqualTo(expectedLabelMap); } @Test public void testMergeResources_Resource1_Null() { Map<String, String> expectedLabelMap = new HashMap<>(); expectedLabelMap.put("a", "1"); expectedLabelMap.put("b", "3"); expectedLabelMap.put("c", "4"); Resource resource = DEFAULT_RESOURCE.merge(null).merge(resource2); assertThat(resource.getLabels()).isEqualTo(expectedLabelMap); } @Test public void testMergeResources_Resource2_Null() { Map<String, String> expectedLabelMap = new HashMap<>(); expectedLabelMap.put("a", "1"); expectedLabelMap.put("b", "2"); Resource resource = DEFAULT_RESOURCE.merge(resource1).merge(null); assertThat(resource.getLabels()).isEqualTo(expectedLabelMap); } @Test public void testCreateResources_ArgumentKey_Null() { Map<String, String> labelMap = new HashMap<>(); labelMap.put("a", "1"); labelMap.put(null, "2"); thrown.expect(NullPointerException.class); Resource.create(labelMap); } @Test public void testCreateResources_ArgumentKey_Unprintable() { Map<String, String> labelMap = new HashMap<>(); labelMap.put("a", "1"); labelMap.put("\2b\3", "2"); thrown.expect(IllegalArgumentException.class); Resource.create(labelMap); } }
以上所述就是小编给大家介绍的《提高单元测试用例覆盖率》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- C++语言的单元测试与代码覆盖率
- James Shore:不要使用单元测试的代码覆盖率
- 使用 PHPUnit 进行单元测试并生成代码覆盖率报告
- 从单元测试覆盖率看富领域模型到底有多富
- iOS 覆盖率检测原理与增量代码测试覆盖率工具实现
- 聊聊前端代码覆盖率
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First 设计模式(中文版)
弗里曼 / O'Reilly Taiwan公司 / 中国电力出版社 / 2007-9 / 98.00元
《Head First设计模式》(中文版)共有14章,每章都介绍了几个设计模式,完整地涵盖了四人组版本全部23个设计模式。前言先介绍这本书的用法;第1章到第11章陆续介绍的设计模式为Strategy、Observer、Decorator、Abstract Factory、Factory Method、Singleton,Command、Adapter、Facade、TemplateMethod、I......一起来看看 《Head First 设计模式(中文版)》 这本书的介绍吧!