JUnit 5 –基础

上周,我们设置了JUnit 5以能够编写测试。 让我们开始吧!

总览

这篇文章是有关JUnit 5的系列文章的一部分:

  • 设定
  • 基本
  • 建筑
  • 条件
  • 注射

在新兴的《 JUnit 5用户指南》中可以找到您将在此处阅读的更多内容以及更多内容。 请注意,它基于Alpha版本,因此可能会发生变化。

确实,我们鼓励我们提出问题或提出请求,以便JUnit 5可以进一步改进。 请利用这个机会! 这是我们帮助JUnit帮助我们的机会,因此,如果您能在这里看到一些改善,请确保将其上游

如有必要,此帖子将得到更新。 我在这里显示的代码示例可以在GitHub上找到

哲学

我们将在另一篇文章中讨论的新体系结构旨在扩展性。 将来有可能使用JUnit 5进行非常陌生的测试技术(至少对我们熟悉的Java开发人员而言)。

但是到目前为止,其基础知识与当前版本4非常相似。JUnit5的表面经过了有意识的逐步改进,开发人员应该有宾至如归的感觉。 至少我愿意,我想你也会:

基本生命周期和功能

class Lifecycle {
 
	@BeforeAll
	static void initializeExternalResources() {
		System.out.println("Initializing external resources...");
	}
 
	@BeforeEach
	void initializeMockObjects() {
		System.out.println("Initializing mock objects...");
	}
 
	@Test
	void someTest() {
		System.out.println("Running some test...");
		assertTrue(true);
	}
 
	@Test
	void otherTest() {
		assumeTrue(true);
 
		System.out.println("Running another test...");
		assertNotEquals(1, 42, "Why wouldn't these be the same?");
	}
 
	@Test
	@Disabled
	void disabledTest() {
		System.exit(1);
	}
 
	@AfterEach
	void tearDown() {
		System.out.println("Tearing down...");
	}
 
	@AfterAll
	static void freeExternalResources() {
		System.out.println("Freeing external resources...");
	}
 
}

看到? 没什么大惊喜。

JUnit 5的基础

能见度

最明显的变化是测试类和方法不再必须公开。 包可见性足以满足要求,而私有可见性则无法满足。 我认为这是一个明智的选择,符合我们理解不同可见性修饰符的方式。

大! 我想说的是,输入的字母更少,但是您还是没有手动进行操作,对吗? 滚动测试类时要忽略的样板更少。

测试生命周期

@测试

最基本的JUnit批注是@Test ,它标记要作为测试运行的方法。

尽管不再使用可选参数,但实际上没有改变。 现在可以通过断言来验证预期的异常,但是据我所知,还没有替代超时的方法

JUnit 5为每个测试方法创建一个新的测试实例(与JUnit 4相同)。

之前和之后

您可能需要运行代码来设置和拆除测试。 有四个方法注释可帮助您实现此目的:

  • @BeforeAll :执行一次; 在标有@BeforeEach的测试和方法之前运行。
  • @BeforeEach :在每次测试之前执行。
  • @AfterEach :在每次测试后执行。
  • @AfterAll :执行一次; 在标有@AfterEach的所有测试和方法之后运行。

因为为每个测试都创建了一个新实例,所以没有明显的实例可以在其上调用@ BeforeAll / @AfterAll方法,因此它们必须是静态的。

带有相同注释的不同方法的执行顺序是不确定的。 据我所知,继承方法也是如此。 当前正在讨论是否应该定义订单。

除了名称外,这些批注的工作方式与JUnit 4中的完全相同。尽管并不 罕见 ,但我并不相信这些名称。 有关详细信息,请参见此问题

禁用测试

今天是星期五下午,您只想回家? 没问题,只需在测试@Disabled@Disabled (可以选择给出一个原因)并运行。

禁用测试

@Test
@Disabled("Y U No Pass?!")
void failingTest() {
	assertTrue(false);
}
测试课程生命周期

与原型相比,有趣的是注意到测试类的生命周期没有进入alpha版本。 它将在测试类的同一实例上运行所有测试,从而允许测试通过改变状态相互交互。

正如我在讨论原型时所写的那样:我认为这是功能的典型案例,在99%的案例中有害,而在另外1%的案例中必不可少。 考虑到可怕的测试间依赖的真正风险,我想将它以原始形式删除是一件好事。

但是JUnit团队正在讨论使用其他名称和添加的语义将其重新引入。 这将使其使用非常刻意。 你怎么看?

断言

如果@Test@Before... ,和@After...是一个测试套件的骨架,断言是它的心脏。 在准备好要测试的实例并在其上执行要测试的功能之后,断言可确保所需的属性成立。 如果没有,则它们将无法通过运行测试。

经典

经典断言要么检查单个实例的属性(例如,它不为null),要么进行某种比较(例如,两个实例相等)。 在这两种情况下,它们都可以选择将消息作为最后一个参数,当断言失败时显示该消息。 如果构造消息很昂贵,则可以将其指定为lambda表达式,因此构造将延迟到实际需要消息之前。

经典断言

@Test
void assertWithBoolean() {
	assertTrue(true);
	assertTrue(this::truism);
 
	assertFalse(false, () -> "Really " + "expensive " + "message" + ".");
}
 
boolean truism() {
	return true;
}
 
@Test
void assertWithComparison() {
	List<String> expected = asList("element");
	List<String> actual = new LinkedList<>(expected);
 
	assertEquals(expected, actual);
	assertEquals(expected, actual, "Should be equal.");
	assertEquals(expected, actual, () -> "Should " + "be " + "equal.");
	
	assertNotSame(expected, actual, "Obviously not the same instance.");
}

如您所见,JUnit 5在这里没有太大变化。 名称与以前相同,比较断言仍然采用一对期望值和实际值(按此顺序)。

期望-实际顺序对于理解测试的失败消息和意图至关重要,但是很容易混淆,这是一个大盲点。 但是,除了创建一个新的断言框架之外,没有什么可做的了。 考虑到像Hamcrestugh !)或AssertJ (yeah!)这样的大公司,这不是投资有限时间的明智方法。 因此,目标是保持声明的重点和精力。

新的是失败消息排在最后。 我喜欢它,因为它始终关注着球,即所主张的财产。 作为对Java 8的致敬,布尔断言现在接受了provider ,这是一个很好的细节。

扩展的

除了检查特定属性的经典断言之外,还有其他一些断言。

第一个甚至不是真正的断言,它只是通过失败消息使测试失败。

'失败'

@Test
void failTheTest() {
	fail("epicly");
}

然后,我们有了assertAll ,它接受可变数量的断言并在报告失败(如果有的话)之前测试所有这些断言。

'assertAll'

@Test
void assertAllProperties() {
	Address address = new Address("New City", "Some Street", "No");
 
	assertAll("address",
			() -> assertEquals("Neustadt", address.city),
			() -> assertEquals("Irgendeinestraße", address.street),
			() -> assertEquals("Nr", address.number)
	);
}

“ AssertAll”的失败消息

org.opentest4j.MultipleFailuresError: address (3 failures)
	expected: <Neustadt> but was: <New City>
	expected: <Irgendeinestraße> but was: <Some Street>
	expected: <Nr> but was: <No>

检查多个相关属性并获取所有相关属性的值非常好,这与常规行为相反,在常规行为中,测试报告第一个失败的属性,而您永远不知道其他值。

最后,我们有了assertThrowsexpectThrows 。 如果给定的方法没有引发指定的异常,则两者都将通过测试。 后者还返回异常,因此可以将其用于进一步的验证,例如断言该消息包含某些信息。

“ assertThrows”和“ excpectThrows”

@Test
void assertExceptions() {
	assertThrows(Exception.class, this::throwing);
 
	Exception exception = expectThrows(Exception.class, this::throwing);
	assertEquals("Because I can!", exception.getMessage());
}

假设条件

假设只允许在某些条件符合预期的情况下运行测试。 这可以用来减少测试套件的运行时间和冗长,尤其是在失败的情况下。

'assumeTrue','assumeFalse'和'assumingThat'

@Test
void exitIfFalseIsTrue() {
	assumeTrue(false);
	System.exit(1);
}
 
@Test
void exitIfTrueIsFalse() {
	assumeFalse(this::truism);
	System.exit(1);
}
 
private boolean truism() {
	return true;
}
 
@Test
void exitIfNullEqualsString() {
	assumingThat(
			"null".equals(null),
			() -> System.exit(1)
	);
}

假设可以用于中止不满足先决条件的测试,或者仅在条件成立时才执行(部分测试)测试。 主要区别在于,中止的测试报告为已禁用,而由于条件不成立而为空的测试为纯绿色。

嵌套测试

JUnit 5使嵌套测试类变得毫不费力。 只需使用@Nested注释内部类, @Nested所有测试方法也将被执行:

'@嵌套'

package org.codefx.demo.junit5;// NOT_PUBLISHED
 
import org.junit.gen5.api.BeforeEach;
import org.junit.gen5.api.Nested;
import org.junit.gen5.api.Test;
 
import static org.junit.gen5.api.Assertions.assertEquals;
import static org.junit.gen5.api.Assertions.assertTrue;
 
class Nest {
	
	int count = Integer.MIN_VALUE;
	
	@BeforeEach
	void setCountToZero() {
		count = 0;
	}
	
	@Test
	void countIsZero() {
		assertEquals(0, count);
	}
	
	@Nested
	class CountGreaterZero {
 
		@BeforeEach
		void increaseCount() {
			count++;
		}
 
		@Test
		void countIsGreaterZero() {
			assertTrue(count > 0);
		}
 
		@Nested
		class CountMuchGreaterZero {
 
			@BeforeEach
			void increaseCount() {
				count += Integer.MAX_VALUE / 2;
			}
 
			@Test
			void countIsLarge() {
				assertTrue(count > Integer.MAX_VALUE / 2);
			}
 
		}
 
	}
	
}

如您所见, @BeforeEach (和@AfterEach )也在这里工作。 尽管当前没有记录,但是初始化是从外而内执行的。 这允许为内部测试逐步构建上下文。

为了使嵌套测试能够访问外部测试类的字段,嵌套类不得为静态。 不幸的是,这禁止使用静态方法,因此在这种情况下不能使用@BeforeAll@AfterAll 。 ( 或者可以吗?

也许您在问自己这有什么用。 我使用嵌套的测试类继承接口测试 ,而其他人则使他们的测试类小型化和集中化JUnit团队通常会给出更复杂的示例来演示后者,该示例测试堆栈:

使用嵌套类测试堆栈

aclass TestingAStack {
 
    Stack<Object> stack;
    boolean isRun = false;
 
    @Test
    void isInstantiatedWithNew() {
        new Stack<Object>();
    }
 
    @Nested
    class WhenNew {
 
        @BeforeEach
        void init() {
            stack = new Stack<Object>();
        }
 
        // some tests on 'stack', which is empty
 
        @Nested
        class AfterPushing {
 
            String anElement = "an element";
 
            @BeforeEach
            void init() {
                stack.push(anElement);
            }
 
            // some tests on 'stack', which has one element...
 
        }
    }
}

在此示例中,状态被连续更改,并且每种情况下都要执行许多测试。

命名测试

JUnit 5带有一个@DisplayName注释,它使开发人员可以为他们的测试类和方法提供更易于@DisplayName名称。

使用它,堆栈示例如下所示:

@DisplayName("A stack")
class TestingAStack {

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() { /*...*/ }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @Test
        @DisplayName("is empty")
        void isEmpty() { /*...*/ }

        @Test
        @DisplayName("throws EmptyStackException when popped")
        void throwsExceptionWhenPopped() { /*...*/ }

        @Test
        @DisplayName("throws EmptyStackException when peeked")
        void throwsExceptionWhenPeeked() { /*...*/ }

        @Nested
        @DisplayName("after pushing an element")
        class AfterPushing {

            @Test
            @DisplayName("it is no longer empty")
            void isEmpty() { /*...*/ }

            @Test
            @DisplayName("returns the element when popped and is empty")
            void returnElementWhenPopped() { /*...*/ }

            @Test
            @DisplayName(
                    "returns the element when peeked but remains not empty")
            void returnElementWhenPeeked(){ /*...*/ }
        }
    }
}

这产生很好的可读的输出,并应带来欢乐的心脏BDD “呃!

junit-5-basic测试堆栈

反射

就是这样,您做到了! 我们快速了解了如何使用JUnit 5的基础知识,现在您知道编写简单测试所需的全部知识:如何注释生命周期方法(使用@[Before|After][All|Each] )和测试方法本身( @Test ),如何嵌套( @Nested )和名称( @DisplayName )测试,以及断言和假设如何工作(与之前类似)。

但是,等等,还有更多! 我们还没有谈论测试方法的条件执行,非常酷的参数注入,扩展机制或项目的体系结构。 而且我们现在不会,因为我们将从JUnit 5短暂休息一下,然后在大约一个月内恢复到原来的状态。

敬请关注!

翻译自: https://www.javacodegeeks.com/2016/03/junit-5-basics.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值