单元测试Junit入门(PDF下载)
编者按:在Java Code Geeks,我们已经提供了大量JUnit的教程,例如JUnit入门示例,使用JUnit的断言和注释例子,JUnit的注释示例等。然而,我们倾向收集所有的JUnit特性在一个详细的指南以方便读者。我们希望你喜欢它!
目录
1.单元测试的介绍
1.1. 什么是单元测试?
一个单位可以是一个函数,一个类,一个包,或者一个子系统。所以,术语单元测试是指测试这样小单位的代码,以确保它们正常工作。例如,我们可以测试在给定的输入或者某给定条件的情况下,输出是否是我们所希望看到的。这种做法可以帮助开发人员发现代码逻辑背后的故障,提高其代码的质量。另外,单元测试可以用来以确保代码在未来变化中可以正常实现逻辑。
1.2. 测试覆盖
一般情况下,开发社区对关于代码应测试的百分比(测试覆盖)有不同的看法。一些开发者认为代码应该有100%的测试覆盖率,而有些则认为有50%或更低的测试覆盖率也可以。在任何情况下,你应该写测试你的代码中复杂或关键部分。
1.3. 在Java单元测试
在Java中最流行的测试框架是JUnit。本指南主要集中于JUnit,下面的章节有更多测试框架的细节。在Java中另一种流行的测试框架是TestNG。
2. JUnit的介绍
JUnit 是一个开源测试框架,用于编写和运行可重复的自动化测试,让我们可以确保我们的代码按预期工作。 JUnit 广泛应用于行业,可以用作单独的Java程序(在命令行)或在一个IDE如Eclipse内。 JUnit提供:
- 断言测试预期结果.
- 测试功能共享通用的测试数据.
- 测试套件轻松地组织和运行测试.
- 图形和文本测试运行.
JUnit用于测试:
- 整个对象
- 某些相互作用的方法的方法或 – 一个对象的一部分
- 几个对象之间的互动
2.1. 使用Eclipse JUnit的简单示例
在本节中,我们将看到一个简单的JUnit例子。首先,我们将测试类,例如测试:
Calculate.java
package com.javacodegeeks.junit; public class Calculate { public int sum(int var1, int var2) { System.out.println("Adding values: " + var1 + " + " + var2); return var1 + var2; } }
在上面的代码中,我们可以看到,类有一个公共方法名字sum()
,它获取两个整数作为输入,将它们添加并返回结果。这里,我们将测试此方法。为了这个目的,我们将创建另一个类,该类包含有方法去测试上一个类中的每一个方法(在这个例子,我们只有一个方法来进行测试)。这是使用的最常见的方式。当然,如果一个方法是非常复杂,我们可以有一个以上的测试方法对付这种复杂的方法。创建测试用例的详细信息将显示在下面的章节。下面,有一个类名为CalculateTest.java
作为我们的测试类角色:
CalculateTest.java
package com.javacodegeeks.junit; import static org.junit.Assert.*; import org.junit.Test; public class CalculateTest { Calculate calculation = new Calculate(); int sum = calculation.sum(2, 5); int testSum = 7; @Test public void testSum() { System.out.println("@Test sum(): " + sum + " = " + testSum); assertEquals(sum, testSum); } }
让我们来解释一下上面的代码。首先,我们可以看到,有一个@Test
注释在testSum()
方法的上方。此注释指示该公共空方法其所在的类可以运行作为一个测试用例。因此,testSum()
是用来测试的sum()
。我们还可以观察到有一个方法称为assertEquals(sum, testsum)
。该方法assertEquals ([String message], object expected, object actual)
声称这两个对象相等。如果我们运行测试类,右键单击,在测试类并选择em>Run As -> Junit Test,程序的输出看起来像:
Adding values: 2 + 5 @Test sum(): 7 = 7
为了看到JUnit测试的实际结果,Eclipse IDE提供了JUnit窗口显示的测试结果。在测试成功的情况下,JUnit窗口不显示任何错误或失败,我们可以看到在下面的图片:
现在,如果我们改变这行代码:
int testSum = 10;
所以如果测试的整数是不相等的,输出将是:
Adding values: 2 + 5 @Test sum(): 7 = 10
在JUnit的窗口,会出现错误消息,将显示:
java.lang.AssertionError: expected: but was: at com.javacodegeeks.junit.CalculateTest.testSum(CalculateTest.java:16)
2.2. JUnit的注释
在这一节中我们将提到JUnit 4支持的基本注解。下表总结了这些注释:
注释 | 描述 |
---|---|
@Test public void method() | 注释Test 表明它所对应的public void方法可以运行作为一个测试用例。 |
@Before public void method() | 注释Before 表明该方法必须在类中的每个测试之前执行,以执行必要的测试的一些前提条件。 |
@BeforeClass public static void method() | 注释BeforeClass 表明该静态方法是针对所有测试,只执行一次。通常这种情况是测试方法共用一些比较占资源的设置(例如连接数据库)。 |
@After public void method() | 注释After 表明该方法在每个测试执行之后执行(例如在每一个测试重置一些变量和删除临时变量等) |
@AfterClass public static void method() | 注释AfterClass 时可以使用的方法需要被执行后的JUnit测试用例类执行所有的测试以清理昂贵的装置(如从数据库断开)。注意:附上注释的方法(类似于课前)必须被定义为静态。 |
@Ignore public static void method() | 注释Ignore 可用于当你想暂时禁用执行一个特定的测试。每一种方法注释@Ignore了的就不会运行。 |
让我们看一个与上面提到的注释有关的测试实例。
AnnotationsTest.java
package com.javacodegeeks.junit; import static org.junit.Assert.*; import java.util.*; import org.junit.*; public class AnnotationsTest { private ArrayList testList; @BeforeClass public static void onceExecutedBeforeAll() { System.out.println("@BeforeClass: onceExecutedBeforeAll"); } @Before public void executedBeforeEach() { testList = new ArrayList(); System.out.println("@Before: executedBeforeEach"); } @AfterClass public static void onceExecutedAfterAll() { System.out.println("@AfterClass: onceExecutedAfterAll"); } @After public void executedAfterEach() { testList.clear(); System.out.println("@After: executedAfterEach"); } @Test public void EmptyCollection() { assertTrue(testList.isEmpty()); System.out.println("@Test: EmptyArrayList"); } @Test public void OneItemCollection() { testList.add("oneItem"); assertEquals(1, testList.size()); System.out.println("@Test: OneItemArrayList"); } @Ignore public void executionIgnored() { System.out.println("@Ignore: This execution is ignored"); } }
如果我们运行上面的测试,控制台输出如下:
@BeforeClass: onceExecutedBeforeAll @Before: executedBeforeEach @Test: EmptyArrayList @After: executedAfterEach @Before: executedBeforeEach @Test: OneItemArrayList @After: executedAfterEach @AfterClass: onceExecutedAfterAll
2.3. JUnit断言
在这一节中我们将提出一些断言方法。所有这些方法都是通过Assert
类扩展类class java.lang.Object
提供,它们为测试而编写以检测故障。在下面的表格中还有对常用的断言方法更加详细的解释。
断言 | 描述 |
---|---|
void assertEquals([String message], expected value, actual value) | 断言两值相等。值可能是int型,short型,long型,byte型,char 型或java.lang.Object对象。第一个参数是一个可选的字符串消息。 |
void assertTrue([String message], boolean condition) | 断言某条件为真。 |
void assertFalse([String message],boolean condition) | 断言某条件是错误的。 |
void assertNotNull([String message], java.lang.Object object) | 断言一个对象不为空。 |
void assertNull([String message], java.lang.Object object) | 断言一个对象是空的。 |
void assertSame([String message], java.lang.Object expected, java.lang.Object actual) | 断言两个对象指向同一个对象。 |
void assertNotSame([String message], java.lang.Object unexpected, java.lang.Object actual) | 断言两个对象不指向同一个对象。 |
void assertArrayEquals([String message], expectedArray, resultArray) | 断言期望数组和结果数组相等。数组的类型可以是int型,short型,long型,byte型,char 型或java.lang.Object对象。 |
让我们看看一些上述断言的例子。
AssertionsTest.java
package com.javacodegeeks.junit; import static org.junit.Assert.*; import org.junit.Test; public class AssertionsTest { @Test public void test() { String obj1 = "junit"; String obj2 = "junit"; String obj3 = "test"; String obj4 = "test"; String obj5 = null; int var1 = 1; int var2 = 2; int[] arithmetic1 = { 1, 2, 3 }; int[] arithmetic2 = { 1, 2, 3 }; assertEquals(obj1, obj2); assertSame(obj3, obj4); assertNotSame(obj2, obj4); assertNotNull(obj1); assertNull(obj5); assertTrue(var1 var2); assertArrayEquals(arithmetic1, arithmetic2); } }
在这个类我们可以看到这些断言方法是如何工作的。
- 该方法
assertEquals()
将返回正常当两个比较的对象是平等的,否则将在JUnit窗口显示返回失败和测试将中止。 assertSame()
和assertNotSame()
测试两个对象是否引用指向相同的对象。- 该方法
assertNull()
和assertNotNull()
测试变量是否为空或非空。 assertTrue()
和assertFalse()
方法测试一个条件或变量是真的还是假的。assertArrayEquals()
将比较这两个数组是否平等的,该方法继续进行如果两者平等。否则,JUnit窗口显示失败和测试将中止。
3. Eclipse中使用JUnit完整的例子
在这一节中我们将展示一个使用JUnit完整的例子。我们会看到了如何创建和运行测试,以及如何使用特定的注释和JUnit断言。
3.1. 初始步骤
让我们创建一个Java项目命名JUnitGuide。在src文件夹,我们单击右键并选择New ->Package,以创建一个新的包命名com.javacodegeeks.junit
,我们将里面定位这个类进行测试。为类测试,它被认为是很好的做法去创建一个新的专用于测试的源文件夹,以便将测试类和被测试类放在不同的源文件夹。为此,右键单击您的项目,选择New -> Source Folder,命名新的源文件夹为test并单击Finish。
你可以很容易地看到有两个源文件夹在您的项目:
你也可以在新建的测试文件夹中创建新的包并称为com.javacodegeeks.junit
,使您的测试类不会位于默认的包,我们下面准备开始!
3.2. 创建被测试的java类
右击文件夹src,创建新的Java类 FirstDayAtSchool.java
. 这是公共方法将被测试的类.
FirstDayAtSchool.java
package com.javacodegeeks.junit; import java.util.Arrays; public class FirstDayAtSchool { public String[] prepareMyBag() { String[] schoolbag = { "Books", "Notebooks", "Pens" }; System.out.println("My school bag contains: " + Arrays.toString(schoolbag)); return schoolbag; } public String[] addPencils() { String[] schoolbag = { "Books", "Notebooks", "Pens", "Pencils" }; System.out.println("Now my school bag contains: " + Arrays.toString(schoolbag)); return schoolbag; } }
3.3. 创建和运行JUnit测试用例
创建一个JUnit测试用例来测试现有的类FirstDayAtSchool.java
,在Package Explorer视图上右键单击并选择New → JUnit Test Case。更改源文件夹以便该类位于test源文件夹里并确保选择New JUnit4 test。
然后,单击“Finish”。如果你的项目不包含在类路径中的JUnit库,如下信息将显示出来为路径添加JUnit库:
下面,有一个名为FirstDayAtSchoolTest.java
代码,这是我们的测试类:
FirstDayAtSchool.java
package com.javacodegeeks.junit; import static org.junit.Assert.*; import org.junit.Test; public class FirstDayAtSchoolTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag1 = { "Books", "Notebooks", "Pens" }; String[] bag2 = { "Books", "Notebooks", "Pens", "Pencils" }; @Test public void testPrepareMyBag() { System.out.println("Inside testPrepareMyBag()"); assertArrayEquals(bag1, school.prepareMyBag()); } @Test public void testAddPencils() { System.out.println("Inside testAddPencils()"); assertArrayEquals(bag2, school.addPencils()); } }
现在我们可以通过右键点击测试类然后选择Run As -> JUnit Test运行测试用例。程序的输出如下:
Inside testPrepareMyBag() My school bag contains: [Books, Notebooks, Pens] Inside testAddPencils() Now my school bag contains: [Books, Notebooks, Pens, Pencils]
JUnit视图没有故障或错误。如果我们换一个数组,它包含超过预期的元素:
String[] bag2 = { "Books", "Notebooks", "Pens", "Pencils", "Rulers"};
我们再次运行测试类,JUnit视图将出现故障:
或者,我们再换一个数组,使其包含比预期不同的元素:
String[] bag1 = { "Books", "Notebooks", "Rulers" };
我们再次运行测试类,JUnit视图将再次出现失败:
3.4. 使用@Ignore
注释
让我们看看在上面的例子中我们如何使用@Ignore
注释。在测试类FirstDayAtSchoolTest
中我们会添加注释@Ignore
到方法testAddPencils()
。以这种方式,我们希望这个方法将被忽略,不会被运行。
package com.javacodegeeks.junit; import static org.junit.Assert.*; import org.junit.Ignore; import org.junit.Test; public class FirstDayAtSchoolTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag1 = { "Books", "Notebooks", "Pens" }; String[] bag2 = { "Books", "Notebooks", "Pens", "Pencils" }; @Test public void testPrepareMyBag() { System.out.println("Inside testPrepareMyBag()"); assertArrayEquals(bag1, school.prepareMyBag()); } @Ignore @Test public void testAddPencils() { System.out.println("Inside testAddPencils()"); assertArrayEquals(bag2, school.addPencils()); } }
下面是输出结果:
Inside testPrepareMyBag() My school bag contains: [Books, Notebooks, Pens]
现在,我们将删除@Ignore
注释的方法,然后注释testAddPencils()
整个类。
package com.javacodegeeks.junit; import static org.junit.Assert.*; import org.junit.Ignore; import org.junit.Test; @Ignore public class FirstDayAtSchoolTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag1 = { "Books", "Notebooks", "Pens" }; String[] bag2 = { "Books", "Notebooks", "Pens", "Pencils" }; @Test public void testPrepareMyBag() { System.out.println("Inside testPrepareMyBag()"); assertArrayEquals(bag1, school.prepareMyBag()); } @Test public void testAddPencils() { System.out.println("Inside testAddPencils()"); assertArrayEquals(bag2, school.addPencils()); } }
整个测试类不会被执行,所以没有结果会显示在控制台输出和JUnit视图:
3.5. 创建测试套件
在本节中,我们将看到如何创建测试套件。一个测试套件集合了来源于不同的类并可以一起使用@RunWith和
@Suite注释的测试用例
。这是非常有用的如果你有很多的测试类,并且你想一起运行这些类,而不是运行一次测试一个类。当一个类被注释@RunWith
,,JUnit会调用指定的类来运行测试而不是使用JUnit的默认执行类。基于之前部分提到的类,我们可以创建两个测试类。一个会测试公共方法prepareMyBag()
和另外一个类测试addPencils()
,因此我们最终将有以下类,:
PrepareMyBagTest.java
package com.javacodegeeks.junit; import org.junit.Test; import static org.junit.Assert.*; public class PrepareMyBagTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag = { "Books", "Notebooks", "Pens" }; @Test public void testPrepareMyBag() { System.out.println("Inside testPrepareMyBag()"); assertArrayEquals(bag, school.prepareMyBag()); } }
AddPencilsTest.java
package com.javacodegeeks.junit; import org.junit.Test; import static org.junit.Assert.*; public class AddPencilsTest { FirstDayAtSchool school = new FirstDayAtSchool(); String[] bag = { "Books", "Notebooks", "Pens", "Pencils" }; @Test public void testAddPencils() { System.out.println("Inside testAddPencils()"); assertArrayEquals(bag, school.addPencils()); } }
现在我们将创建一个测试套件以便一起运行上面的类。右键单击文件夹test,创建新的Java类命名suitetest.java如下:
SuiteTest.java
package com.javacodegeeks.junit; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ PrepareMyBagTest.class, AddPencilsTest.class }) public class SuitTest { }
随着@Suite.SuiteClasses注释你可以定义哪些测试类将包含在执行中。所以,如果你右击并选择测试套件 Run As -> JUnit Test,测试类的执行将按@Suite.SuiteClasses
注释定义的顺序来执行。
3.6. 创建参数化测试
在这一节中我们将看到如何创建参数化测试。为此,我们将使用在2.1节中提到的提供了一个公共方法添加整数的类,这将是被测试的类。但是什么时候一个测试类可以被视为一个参数化测试类?当然,当它满足下列要求:
- 这类注释了
@RunWith(Parameterized.class)
。正如在上一节中,@RunWith
注释使JUnit来调用类中的标签来运行测试,而不是使用JUnit默认执行类。Parameterized
是JUnit一个执行类,可以根据不同的输入运行相同的测试用例。 - 该类有一个构造函数存储测试数据。
- 该类有一个静态方法注释了
@Parameters
,生成并返回测试数据。 - 该类有个测试,这显然意味着它需要一个方法注释了
@Test
。
现在,我们将遵循上述指导创建一个新的测试类命名CalculateTest.java
这类的源代码如下。
CalculateTest.java
package com.javacodegeeks.junit; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class CalculateTest { private int expected; private int first; private int second; public CalculateTest(int expectedResult, int firstNumber, int secondNumber) { this.expected = expectedResult; this.first = firstNumber; this.second = secondNumber; } @Parameters public static Collection addedNumbers() { return Arrays.asList(new Integer[][] { { 3, 1, 2 }, { 5, 2, 3 }, { 7, 3, 4 }, { 9, 4, 5 }, }); } @Test public void sum() { Calculate add = new Calculate(); System.out.println("Addition with parameters : " + first + " and " + second); assertEquals(expected, add.sum(first, second)); } }
我们可以观察上面的类,它满足上述所有要求。方法addedNumbers
注释 @Parameters
返回一个数组集合。每个数组包括每个测试要执行的输入和输出。在每个数组元素个数必须和构造函数的参数数量是相同的。所以,在这种特定的情况下,每个阵列包括三个元素表示增加的元素和结果。如果我们运行calculatetest测试用例,控制台输出如下:
Addition with parameters : 1 and 2 Adding values: 1 + 2 Addition with parameters : 2 and 3 Adding values: 2 + 3 Addition with parameters : 3 and 4 Adding values: 3 + 4 Addition with parameters : 4 and 5 Adding values: 4 + 5
正如我们在输出中看到,测试用例执行四次,这是在该方法中标签@Parameters
的数量。
3.7. 规则
本节我们介绍JUnit的一个新特性叫Rules,它允许在一个测试类里非常灵活地增加或重新定义每个测试方法的行为。为此,标签@Rule
应该用来标记测试类的公共属性。这些属性应该是类型MethodRule
,是测试方法运行报告。多MethodRules
可应用于测试方法。MethodRule
接口有很多实现,如ErrorCollector
使测试在第一个问题被发现继续执行,ExpectedException指定了
异常类型和消息的测试参数,TestName
测试名字里面有测试方法,以及其他许多人。除了那些已定义的规则,开发人员可以创建自己的自定义规则和他们的测试用例,用他们的愿望。下面我们提出的方法,我们可以使用一个现有的规则,在我们自己的测试名为TestName
。测试开始时候会触发TestName
。
NameRuleTest.java
package com.javacodegeeks.junit; import static org.junit.Assert.*; import org.junit.*; import org.junit.rules.TestName; public class NameRuleTest { @Rule public TestName name = new TestName(); @Test public void testA() { System.out.println(name.getMethodName()); assertEquals("testA", name.getMethodName()); } @Test public void testB() { System.out.println(name.getMethodName()); assertEquals("testB", name.getMethodName()); } }
我们可以看到,标签@Rule
标记了公共属性name
是MethodRule
型,更具体地说是TestName
类型。然后,我们可以把它name
用在我们的测试中和找到这个名称的测试方法作为例子。
3.8. 类别
JUnit的另一个新的功能称为类别,它允许你把某些测试组在一起,甚至排除另外一些组(类别)。例如,您可以单独测试慢例子。@Category标签可以用来指定一个测试用例或一个方法在这些类别里面。下面是一个基于JUnit 4.8我们如何利用JUnit这样特征的例子。
public interface FastTests { /* category marker */ }
public interface SlowTests { /* category marker */ }
首先,我们定义了两个类别,FastTests 和SlowTests。一个类别可以是类或接口。
public class A { @Test public void a() { fail(); } @Category(SlowTests.class) @Test public void b() { } }
在上面的代码中,我们用标签@Category
注释标记类A
的测试方法b()
,从而表明b()这个特定的方法属于类别SlowTests
。所以,我们不仅可以标记整个类,也可以单独标记他们的一些测试方法。
@Category({ SlowTests.class, FastTests.class }) public class B { @Test public void c() { } }
在上面的示例代码,我们可以看到整个类B注释了@Category
。注释测试类@Category会自动包括在这类别里的所有测试方法。我们也可以看到,一个测试类或测试方法可以属于多个类别。
@RunWith(Categories.class) @IncludeCategory(SlowTests.class) @SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b and B.c, but not A.a }
此示例中的代码,我们注意到有一个名叫SlowTestSuite
的套件测试。基本上,类是一种测试套件。在这个测试套件,我们观察到属于类别SlowTests 的方法会被执行。在这种特定的情况下,属于slowtests分类方法将被执行。因此,只有A类测试方法b()以及B类测试方法c()会执行,这都属于SlowTests 类别。
@RunWith(Categories.class) @IncludeCategory(SlowTests.class) @ExcludeCategory(FastTests.class) @SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b, but not A.a or B.c }
最后,我们改变测试套件一点,我们添加一个新的标签@ExcludeCategory
说明该类将被排除在执行外。在这种特定的情况下,只有类A
的测试方法b()
将被执行,因为这是唯一明确属于SlowTests的测试方法。我们注意到,在这两种情况下,类A
的测试方法a()
不会执行是因为它不属于任何类别。
4. 在命令行运行JUnit测试
你可以用利用org.junit.runner.JUnitCore
类在Eclipse外运行JUnit测试。这类提供runClasses()
方法允许你执行一个或多个测试类。runClasses()
方法的返回类型是一个对象类型org.junit.runner.Result
。这个对象可以用来收集有关测试的信息。同时,如果这是一个失败的测试,你可以使用对象org.junit.runner.notification.Failure
,它包含有对测试失败的描述。下面的过程显示了如何在Eclipse外运行你的测试。下面的代码创建一个新的Java类命名JunitRunner.java
:
JunitRunner.java
package com.javacodegeeks.junit; import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; public class JunitRunner { public static void main(String[] args) { Result result = JUnitCore.runClasses(AssertionsTest.class); for (Failure fail : result.getFailures()) { System.out.println(fail.toString()); } if (result.wasSuccessful()) { System.out.println("All tests finished successfully..."); } } }
作为一个例子,我们选择运行测试类AssertionsTest
。
- 打开命令行,向下移动提示符并找到两类所在的目录。
- 编译Test 类和Runner类。
C:\Users\konstantina\eclipse_luna_workspace\JUnitGuide\test\com\javacodegeeks\junit>javac -classpath "C:\Users\konstantina\Downloads\junit-4.11.jar";"C:\Users\konstantina\Downloads\hamcrest-core-1.3.jar"; AssertionsTest.java JunitRunner.java
就像我们在Eclipse中所做的一样,项目的路径还应包括JUnit的library jars。
- 现在运行
JunitRunner
.
C:\Users\konstantina\eclipse_luna_workspace\JUnitGuide\test\com\javacodegeeks\junit>java -classpath "C:\Users\konstantina\Downloads\junit-4.11.jar";"C:\Users\konstantina\Downloads\hamcrest-core-1.3.jar"; JunitRunner
输出:
All tests finished successfully...
5. 结论
这是关于JUnit测试框架的详细指南,Java最流行的测试框架。如果你喜欢这个,可以订阅我们的通讯,享受每周更新的免费白皮书!同时可以在 JCG Academy浏览更多的训练!
Translated by: Klaus Cai |
This post is a translation of JUnit Tutorial for Unit Testing – The ULTIMATE Guide from Konstantina Dimtsa |