07 AssertJ

[toc]

AssertJ一分钟入门 - 简书
AssertJ / Fluent assertions for java

maven

1
2
3
4
5
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.11.1</version>
</dependency>

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package AssertJ;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

//import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThat;

public class AssertJTest {
public static void main(String[] args) {
assertThat("aa".equals("aa"));
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3);
assertThat(list).contains(1);
assertThat(list).containsOnly(1, 2, 3);
}
}

Basic tips :

Java 8 assertions, see release notes : 3.8.0 / 3.7.0 / 3.6.0 / 3.5.0 / 3.4.0 / 3.3.0 /3.2.0 / 3.1.0 / 3.0.0

IDE configuration to directly get assertThat in code completion

Describe your assertion using as(String description, Object… args)

call as() before the assertion

1
2
3
assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(100);
// The error message starts with the given description in [] :
// [check Frodo's age] expected:<100> but was:<33>

Exception assertions guide

Using String assertions on the content of a file

Iterable and arrays assertions :

Combining filtering and assertions on iterables or arrays

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Filtering can be done on arrays or iterables. Filter criteria are expressed by :
// - a Java 8 Predicate
// using simple predicate(谓语?), best expressed with a lambda
assertThat(fellowshipOfTheRing).filteredOn( character -> character.getName().contains("o") )
.containsOnly(aragorn, frodo, legolas, boromir);
// - Filtering on a property or a field
assertThat(fellowshipOfTheRing).filteredOn("race", MAN)
.filteredOn("name", not("Boromir"))
.containsOnly(aragorn);
// - Filtering with a Condition
import org.assertj.core.api.Condition;

Condition<Player> mvpStats= new Condition<Player>() {
@Override
public boolean matches(Player player) {
return player.pointsPerGame() > 20 && (player.assistsPerGame() >= 8 || player.reboundsPerGame() >= 8);
}
};

List<Player> players;
players.add(rose); // Derrick Rose : 25 ppg - 8 assists - 5 rebounds
players.add(lebron); // Lebron James : 27 ppg - 6 assists - 9 rebounds
players.add(noah); // Joachim Noah : 8 ppg - 5 assists - 11 rebounds

assertThat(players).filteredOn(mvpStats)
.containsOnly(rose, lebron);

Assertions on extracted properties/fields of iterable/array elements

1
2
3
4
5
6
7
8
9
10
11
12
assertThat(fellowshipOfTheRing).extracting("name")
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");

// when checking several properties/fields you have to use tuples :
import static org.assertj.core.api.Assertions.tuple;

// extracting name, age and and race.name nested property
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
.contains(tuple("Boromir", 37, "Man"),
tuple("Sam", 38, "Hobbit"),
tuple("Legolas", 1000, "Elf"));

Flat(map) extracting

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
List<Player> reallyGoodPlayers = list(jordan, magic);

// check all team mates by specifying the teamMates property (Player has a getTeamMates() method):
assertThat(reallyGoodPlayers).flatExtracting("teamMates")
.contains(pippen, kukoc, jabbar, worthy);

// alternatively, you can implement an Extractor to extract the team mates:
assertThat(reallyGoodPlayers).flatExtracting(teamMates)
.contains(pippen, kukoc, jabbar, worthy);

// where teamMates is an instance of PlayerTeammatesExtractor:
public class PlayerTeammatesExtractor implements Extractor<Player, List<Player>> {
@Override
public List<Player> extract(Player input) {
return input.getTeamMates();
}
}

Assertions on results of a method call on iterable/array elements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Like extracting but instead of extracting properties/fields
// ** it extracts the result of a given method invocation on the elements of the Iterable/Array under test and puts the results into a new Iterable/Array which becomes the object under test.
// WesterosHouse class has a method: public String sayTheWords()
List<WesterosHouse> greatHouses = new ArrayList<WesterosHouse>();
greatHouses.add(new WesterosHouse("Stark", "Winter is Coming"));
greatHouses.add(new WesterosHouse("Lannister", "Hear Me Roar!"));
greatHouses.add(new WesterosHouse("Greyjoy", "We Do Not Sow"));
greatHouses.add(new WesterosHouse("Baratheon", "Our is the Fury"));
greatHouses.add(new WesterosHouse("Martell", "Unbowed, Unbent, Unbroken"));
greatHouses.add(new WesterosHouse("Tyrell", "Growing Strong"));

// let's verify the words of the great houses of Westeros:
// 对每个对象调用 sayTheWords 方法
assertThat(greatHouses).extractingResultOf("sayTheWords")
.contains("Winter is Coming", "We Do Not Sow", "Hear Me Roar")
.doesNotContain("Lannisters always pay their debts");

Advanced tips :

Gather all errors message with soft assertions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Using soft assertions, AssertJ collects all assertion errors instead of stopping at the first one.
// use SoftAssertions instead of direct assertThat methods
SoftAssertions softly = new SoftAssertions();
softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
softly.assertThat(mansion.kitchen()).as("Kitchen").isEqualTo("clean");
softly.assertThat(mansion.library()).as("Library").isEqualTo("clean");
softly.assertThat(mansion.revolverAmmo()).as("Revolver Ammo").isEqualTo(6);
softly.assertThat(mansion.candlestick()).as("Candlestick").isEqualTo("pristine");
softly.assertThat(mansion.colonel()).as("Colonel").isEqualTo("well kempt");
softly.assertThat(mansion.professor()).as("Professor").isEqualTo("well kempt");
// Don't forget to call SoftAssertions global verification !
softly.assertAll();
// When the collected assertions are all asserted together they yield a more descriptive error message:
// org.assertj.core.api.SoftAssertionError:
// The following 4 assertions failed:
// 1) [Living Guests] expected:<[7]> but was:<[6]>
// 2) [Library] expected:<'[clean]'> but was:<'[messy]'>
// 3) [Candlestick] expected:<'[pristine]'> but was:<'[bent]'>
// 4) [Professor] expected:<'[well kempt]'> but was:<'[bloodied and dishevelled]'>

// AssertJ also provides a few ways to avoid having to call softly.assertAll() manually:

// JUnitSoftAssertions with a JUnit rule
@Rule
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();

@Test
public void host_dinner_party_where_nobody_dies() {
Mansion mansion = new Mansion();
mansion.hostPotentiallyMurderousDinnerParty();
// use SoftAssertions instead of direct assertThat methods
softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
// ...
// No need to call softly.assertAll(), it is automatically done by JUnitSoftAssertions rule
}
// AutoCloseableSoftAssertions
@Test
public void host_dinner_party_where_nobody_dies() {
Mansion mansion = new Mansion();
mansion.hostPotentiallyMurderousDinnerParty();
try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
// ...
// no need to call assertAll, it is done when softly is closed.
}
}
// Using the static assertSoftly method
@Test
public void host_dinner_party_where_nobody_dies() {
Mansion mansion = new Mansion();
mansion.hostPotentiallyMurderousDinnerParty();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
// ...
// no need to call assertAll, it is done by assertSoftly.
});
}

Using String assertions on the content of a file

1
2
3
4
5
6
7
File xFile = writeFile("xFile", "The Truth Is Out There");

// classic File assertions
assertThat(xFile).exists().isFile().isRelative();

// String assertions on the file content : contentOf() comes from Assertions.contentOf static import
assertThat(contentOf(xFile)).startsWith("The Truth").contains("Is Out").endsWith("There");

Exception assertions guide

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
assertThatThrownBy(() -> { throw new Exception("boom!"); }).isInstanceOf(Exception.class)
.hasMessageContaining("boom");
// 或者
assertThatExceptionOfType(IOException.class).isThrownBy(() -> { throw new IOException("boom!"); })
.withMessage("%s!", "boom")
.withMessageContaining("boom")
.withNoCause();

// 或者
// when
Throwable thrown = catchThrowable(() -> { throw new Exception("boom!"); });

// then
assertThat(thrown).isInstanceOf(Exception.class)
.hasMessageContaining("boom");

// 无错
assertThatCode(() -> {
// code that should throw an exception
...
}).doesNotThrowAnyException();

Using a custom comparison strategy in assertions

1
2
3
4
5
6
7
8
9
10
11
12
// usingComparator(Comparator) example :

// frodo and sam are instances of TolkienCharacter with Hobbit race (obviously :)), they are not equal
assertThat(frodo).isNotEqualTo(sam);
// ... but if we compare race only, they are (raceComparator implements Comparator<TolkienCharacter>)
assertThat(frodo).usingComparator(raceComparator).isEqualTo(sam);

// usingElementComparator(Comparator) example :
// standard comparison : the fellowshipOfTheRing includes Gandalf but not Sauron (believe me) ...
assertThat(fellowshipOfTheRing).contains(gandalf).doesNotContain(sauron);
// ... but if we compare race only, Sauron is in fellowshipOfTheRing (he's a Maia like Gandalf)
assertThat(fellowshipOfTheRing).usingElementComparator(raceComparator).contains(sauron);

Field by field comparisons

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter frodoClone = new TolkienCharacter("Frodo", 33, HOBBIT);
// 1. isEqualToComparingFieldByField
// Fail as equals compares object references
assertThat(frodo).isEqualsTo(frodoClone);
// frodo and frodoClone are equal when doing a field by field comparison.
assertThat(frodo).isEqualToComparingFieldByField(frodoClone);

// 2. isEqualToComparingOnlyGivenFields
// frodo and sam both are hobbits, so they are equal when comparing only race
assertThat(frodo).isEqualToComparingOnlyGivenFields(sam, "race"); // OK
// they are also equals when comparing only race name (nested field).
assertThat(frodo).isEqualToComparingOnlyGivenFields(sam, "race.name"); // OK
// ... but not when comparing both name and race
assertThat(frodo).isEqualToComparingOnlyGivenFields(sam, "name", "race"); // FAIL

// 3. isEqualToIgnoringGivenFields
// frodo and sam are equal when ignoring name and age as the only remaining field is race
assertThat(frodo).isEqualToIgnoringGivenFields(sam, "name", "age"); // OK both are HOBBIT
// ... but they are not equals if only age is ignored as their names differ.
assertThat(frodo).isEqualToIgnoringGivenFields(sam, "age"); // FAIL

// 4. isEqualToIgnoringNullFields
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);
TolkienCharacter mysteriousHobbit = new TolkienCharacter(null, 33, HOBBIT);
// Null fields in expected object are ignored, mysteriousHobbit has a null name thus it's ignored
assertThat(frodo).isEqualToIgnoringNullFields(mysteriousHobbit); // OK
// ... but this is not reversible !
assertThat(mysteriousHobbit).isEqualToIgnoringNullFields(frodo); // FAIL

Using a custom representation in assertions

1
2


Extending assertions with conditions

1
2


Creating assertions for your domain

1
2


06 joda time

[toc]

Introduction to Joda-Time | Baeldung
使用Joda-Time优雅的处理日期时间 - 简书
Joda-Time使用手册 - 后端 - 掘金

核心类介绍

下面介绍5个最常用的date-time类:

  • Instant - 不可变的类,用来表示时间轴上一个瞬时的点
  • DateTime - 不可变的类,用来替换JDK的Calendar类
  • LocalDate - 不可变的类,表示一个本地的日期,而不包含时间部分(没有时区信息)
  • LocalTime - 不可变的类,表示一个本地的时间,而不包含日期部分(没有时区信息)
  • LocalDateTime - 不可变的类,表示一个本地的日期-时间(没有时区信息)

maven

1
2
3
4
5
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.5</version>
</dependency>

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package jodaTime;

import org.joda.time.DateTime;

import java.util.Date;

public class JodaTime {
public static void main(String[] args) {
// 1. 构造一个DateTime实例
// now
DateTime dateTime1 = new DateTime();
// 指定年月日
DateTime dateTime2 = new DateTime(2020, 2, 23, 18, 12, 0);
// long
DateTime dateTime3 = new DateTime(12345678910L);
// java.util.date
DateTime dateTime4 = new DateTime(new Date());
// ?
DateTime dateTime5 = new DateTime("2016-02-15T00:00:00.000+08:00");

// with 设定时间
dateTime1.withYear(2019);
// plus minus 加减时间
dateTime1.plusDays(5);

// 获取 property
dateTime1.monthOfYear().getAsText();
dateTime1.dayOfWeek().getAsText();

// 因为当时那个地区执行夏令时的原因,在添加一个Period的时候会添加23个小时。
// 而添加一个Duration,则会精确地添加24个小时,而不考虑历法。
// 所以,Period和Duration的差别不但体现在精度上,也同样体现在语义上。
// 因为,有时候按照有些地区的历法 1天 不等于 24小时。

// 因此,用 Duration 不用 Period

// 实例
// 例一 计算上一个月的最后一天
new DateTime().minusMonths(1).dayOfMonth().withMaximumValue();
// 例二 获得任何一年中的第 11 月的第一个星期一的日期,而这天必须是这个月的第一个星期一之后
new DateTime().monthOfYear().setCopy(11)
.dayOfMonth().withMinimumValue()
.plusDays(6)
.dayOfWeek()
.setCopy(1) // set to monday (it will round down)
.plusDays(1);
// 例三 计算五年后的第二个月的最后一天
new DateTime().plusYears(5).monthOfYear().setCopy(2).dayOfMonth().withMaximumValue();

}
}

:D 一言句子获取中...