-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathassertj-core-custom-assertions.html
325 lines (250 loc) · 17.7 KB
/
assertj-core-custom-assertions.html
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="AssertJ site">
<meta name="author" content="Joel Costigliola">
<title>AssertJ / Fluent assertions for java</title>
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Inconsolata|Source+Code+Pro|Open+Sans|Ubuntu|Varela+Round|Karla">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="font-awesome/css/font-awesome.min.css" rel="stylesheet">
<script src="highlight/highlight.pack.js"></script>
<link rel="stylesheet" href="highlight/styles/railscasts.css">
<script>hljs.initHighlightingOnLoad();</script>
<link href="css/assertj.min.css" rel="stylesheet">
<link rel="shortcut icon" href="favicon.png" />
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- You'll want to use a responsive image option so this logo looks good on devices - I recommend using something like retina.js (do a quick Google search for it and you'll find it) -->
<a class="navbar-brand" href="index.html">AssertJ</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="assertj-core-quick-start.html">Quick start</a></li>
<li><a href="assertj-news.html">News</a></li>
<li><a href="assertj-core.html">Core</a></li>
<li><a href="assertj-assertions-generator.html">Assertions generator</a></li>
<li><a href="assertj-guava.html">Guava</a></li>
<li><a href="assertj-joda-time.html">Joda-Time</a></li>
<li><a href="assertj-db.html">DB</a></li>
<li><a href="assertj-neo4j.html">Neo4j</a></li>
<li><a href="assertj-swing.html">Swing</a></li>
<li><a href="assertj-help.html">Help</a></li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="row">
<div class="col-md-2 assertj-sidebar-menu">
<div class="bs-sidebar hidden-print affix-top" role="complementary">
<ul class="bs-sidenav nav ">
<li class="sidenav-header">Main</li>
<li><a href="assertj-core.html">Overview</a></li>
<li><a href="assertj-core-quick-start.html">Quick start</a></li>
<li><a href="assertj-core-news.html">News & releases</a></li>
<li><a href="assertj-core-features-highlight.html">Features highlight</a></li>
<li><a href="assertj-core-conditions.html">Using conditions</a></li>
<li><a href="assertj-core-custom-assertions.html">Custom assertions</a></li>
<li><a href="http://joel-costigliola.github.io/assertj/core/api/index.html" target="_blank">Javadoc (2.x)</a></li>
<li><a href="http://joel-costigliola.github.io/assertj/core-8/api/index.html" target="_blank">Javadoc (3.x)</a></li>
<li><a href="assertj-core.html#help">Help & F.A.Q</a></li>
<li><a href="assertj-core.html#code">Code & issues <i class="fa fa-github"></i></a></li>
<li><a href="assertj-core.html#team">Team</a></li>
<li><a href="assertj-core.html#contributing">Contributing</a></li>
<li class="sidenav-header">Migrating</li>
<li><a href="assertj-core-converting-junit-assertions-to-assertj.html">JUnit 4 Assertions</a></li>
<li><a href="assertj-core-converting-junit5-assertions-to-assertj.html">JUnit 5 Assertions</a></li>
<li><a href="assertj-core-converting-testng-assertions-to-assertj.html">TestNG Assertions</a></li>
<li><a href="assertj-core-migrating-from-fest.html">Fest Assert</a></li>
</ul>
</div>
</div>
<div class="col-lg-10 col-md-10 col-sm-10 text-left" >
<h1 class="page-header">AssertJ Core new website!</h1>
<h2 style="color:darkblue;font-style: italic;">AssertJ Core site has moved to <a href="https://assertj.github.io/doc/">https://assertj.github.io/doc/</a></h2>
<h1 class="page-header">Creating assertions specific to your classes</h1>
<p>Having the possibility to create assertions specific to your own classes is important because it makes your test assertions reflect the domain model. It's a way to use Domain Driven Design ubiquitous language in your tests. Custom assertions can be seen as a DSL of domain assertions.</p>
<p>Writing your own assertions is simple : create a class inheriting from <span class="small-code">AbstractAssert</span> and add your custom assertions methods.<br>
To be easy to use, you should also add a static method <span class="small-code">assertThat</span> to provide an handy entry point to your new assertion class.
</p>
<p>If you have a lot of classes you want to have assertions for, consider using <a href="assertj-assertions-generator.html">AssertJ assertions generator</a>.</p>
<h5>Sections:</h5>
<ul>
<li><a href="#custom-assertion-class">Creating your own assertion class</a></li>
<li><a href="#custom-assertion-entry-point">Providing an entry point for all your custom assertions</a></li>
<li><a href="#single-assertion-entry-point">Providing an entry point for all your custom assertions and AssertJ ones ?</a></li>
<li><a href="#custom-soft-assertions">Enabling soft assertions on your custom assertions</a></li>
</ul>
<h3 class="page-header"><span id="custom-assertion-class"></span> Creating your own assertion class</h3>
<p>Let's see how to do that with an example !<br>
The example is taken from <a href="https://github.com/joel-costigliola/assertj-examples/">assertj-examples</a> and more specifically <a href="https://github.com/joel-costigliola/assertj-examples/blob/master/assertions-examples/src/test/java/org/assertj/examples/custom/TolkienCharacterAssert.java">TolkienCharacterAssert.java</a>.
</p>
<p>We want to have assertion for the <span class="small-code">TolkienCharacter</span> domain model class shown below :</p>
<pre><code class="java">// getter/setter omitted for brevity
public class TolkienCharacter {
private String name;
private Race race;
private int age;
}</code></pre>
<p>Let's name our assertion class <span class="small-code">TolkienCharacterAssert</span>. To follow AssertJ conventions,
we make it inherit from <span class="small-code">AbstractAssert</span> and specify two generic parameters, the first is the class itself (needed for assertion chaining) and the second is the class we want to make assertions on : <span class="small-code">TolkienCharacter</span>.</p>
<p>Inheriting from <span class="small-code">AbstractAssert</span> will give you all the basic assertions : <span class="small-code">isEqualTo</span>, <span class="small-code">isNull</span>, ...</p>
<pre><code class="java">// javadoc omitted for brevity
// 1 - inherits from AbstractAssert !
public class TolkienCharacterAssert extends AbstractAssert<TolkienCharacterAssert, TolkienCharacter> {
// 2 - Write a constructor to build your assertion class with the object you want make assertions on.
public TolkienCharacterAssert(TolkienCharacter actual) {
super(actual, TolkienCharacterAssert.class);
}
// 3 - A fluent entry point to your specific assertion class, use it with static import.
public static TolkienCharacterAssert assertThat(TolkienCharacter actual) {
return new TolkienCharacterAssert(actual);
}
// 4 - a specific assertion !
public TolkienCharacterAssert hasName(String name) {
// check that actual TolkienCharacter we want to make assertions on is not null.
isNotNull();
// check condition
if (!Objects.equals(actual.getName(), name)) {
failWithMessage("Expected character's name to be <%s> but was <%s>", name, actual.getName());
}
// return the current assertion for method chaining
return this;
}
// 4 - another specific assertion !
public TolkienCharacterAssert hasAge(int age) {
// check that actual TolkienCharacter we want to make assertions on is not null.
isNotNull();
// check condition
if (actual.getAge() != age) {
failWithMessage("Expected character's age to be <%s> but was <%s>", age, actual.getAge());
}
// return the current assertion for method chaining
return this;
}
}</code></pre>
<p><span class="small-code">TolkienCharacter</span> assertions usage :</p>
<pre><code class="java">// use assertThat from TolkienCharacterAssert to check TolkienCharacter
TolkienCharacterAssert.assertThat(frodo).hasName("Frodo");
// code is more elegant when TolkienCharacterAssert.assertThat is imported statically :
assertThat(frodo).hasName("Frodo");</code></pre>
<p>Well, that was nice, but having to add a static import for each <span class="small-code">assertThat</span> method of you custom assertion classes is not very handy, it would be better to have a unique assertion entry point. This is what we are goint to see in the next section.</p>
<h3 class="page-header"><span id="custom-assertion-entry-point"></span> Providing an entry point for all your custom assertions</h3>
<p>Now that you have a bunch of custom assertions classes, you want to access them easily. Just create an <span class="small-code">Assertions</span> class providing static <span class="small-code">assertThat</span> methods for each of your assertions classes. For example:</p>
<pre><code class="java">public class MyProjectAssertions {
// give access to TolkienCharacter assertion
public static TolkienCharacterAssert assertThat(TolkienCharacter actual) {
return new TolkienCharacterAssert(actual);
}
// give access to TolkienCharacter Race assertion
public static RaceAssert assertThat(Race actual) {
return new RaceAssert(actual);
}
}</code></pre>
<p>Usage:</p>
<pre><code class="java">// static import to use your custom assertions
import static my.project.MyProjectAssertions.assertThat;
// static import to use AssertJ core assertions
import static org.assertj.core.api.Assertions.assertThat;
...
@Test
public void successful_custom_assertion_example() {
// assertThat(TolkienCharacter) comes from my.project.MyProjectAssertions.assertThat
assertThat(frodo).hasName("Frodo");
// assertThat(String) comes from org.assertj.core.api.Assertions.assertThat
assertThat("frodo").contains("do");
}</code></pre>
<p>Note that if you use <a href="assertj-assertions-generator.html">AssertJ assertions generator</a>, this class will be generated for you.</p>
<h3 class="page-header"><span id="single-assertion-entry-point"></span> Providing an entry point for all your custom assertions and AssertJ ones ?</h3>
<p>This is possible <b>if you only have one entry point class for your custom assertions classes !</b></p>
<p>Let's say it's the case, and your entry point class is <span class="small-code">MyProjectAssertions</span>, make it inherit from <span class="small-code">org.assertj.core.api.Assertions</span> so that when you import statically <span class="small-code">MyProjectAssertions.assertThat</span> you will be able to access all your custom assertions and Assert Core ones !</p>
<pre><code class="java">import org.assertj.core.api.Assertions;
public class MyProjectAssertions extends Assertions {
// same code as before
}</code></pre>
<p>Usage:</p>
<pre><code class="java">// static import to use your custom assertions AND AssertJ core assertions
import static my.project.MyProjectAssertions.assertThat;
...
@Test
public void successful_custom_assertion_example() {
// assertThat(TolkienCharacter) comes from my.project.MyProjectAssertions.assertThat
assertThat(frodo).hasName("Frodo");
// assertThat(String) also comes from my.project.MyProjectAssertions.assertThat
// no need to import statically org.assertj.core.api.Assertions.assertThat;
assertThat("frodo").contains("do");
}</code></pre>
<h5>The problem with several entry point classes inheriting from AssertJ <span class="small-code">Assertions</span></h5>
<p>So you have two entry point classes <span class="small-code">MyAssertions</span> and <span class="small-code">MyOtherAssertions</span> both inheriting from <span class="small-code">org.assertj.core.api.Assertions</span>. You use them in a test like in the code below. Problem ! Java can't choose which <span class="small-code">assertThat(String)</span> method to use, the one from <span class="small-code">MyAssertions</span> or the one <span class="small-code">MyOtherAssertions</span> ?</p>
<pre><code class="java">// both MyAssertions and MyOtherAssertions inherit from org.assertj.core.api.Assertions
import static my.project.MyAssertions.assertThat;
import static my.project.MyOtherAssertions.assertThat;
...
@Test
public void ambiguous_assertThat_resolution() {
// ERROR : assertThat(String) is ambiguous !
// assertThat(String) is available from MyAssertions AND MyOtherAssertions
// (it is defined in Assertions the base class of both MyAssertions and MyOtherAssertions)
assertThat("frodo").contains("do");
}</code></pre>
<p>The solution is simple, don't make your classes inherit from <span class="small-code">org.assertj.core.api.Assertions</span> and use <span class="small-code">import static org.assertj.core.api.Assertions.assertThat</span> to access core assertions.</p>
<h3 class="page-header"><span id="custom-soft-assertions"></span>Enabling soft assertions on your custom assertion classes.</h3>
<p>You may want to perform <a href="assertj-core-features-highlight.html#soft-assertions">soft assertions</a> using your custom assertions. However, the provided <span class="small-code">SoftAssertions</span> class does not know about your custom classes. For example:</p>
<pre><code class="java">SoftAssertions softly = new SoftAssertions();
softly.assertThat(frodo).hasName("frodo");
// The previous line does not compile, as softly.assertThat(frodo) returns an ObjectAssert</code></pre>
<p>To enable soft assertions for your custom assertion classes, you will need to implement a class that extends <span class="small-code">SoftAssertions</span>, and include an <span class="small-code">assertThat</span> method for every class that you want to be able to softly assert on.</p>
<p>The class should look like this:</p>
<pre><code class="java">// Javadocs and imports omitted for brevity
public class MyProjectSoftAssertions extends SoftAssertions {
public TolkienCharacterAssert assertThat(TolkienCharacter actual) {
return proxy(TolkienCharacterAssert.class, TolkienCharacter.class, actual);
}
// additional custom assertions as desired.
}</code></pre>
<p>Note that each <span class="small-code">assertThat</span> method must return an assertion object generated by a call the <span class="small-code">proxy</span> method. The arguments for the call to <span class="small-code">proxy</span> are always the class of the custom assertion object being returned, the class of the object being asserted on, and the object being asserted on itself.</p>
<p>Extending <span class="small-code">SoftAssertions</span> allows you to softly assert on the classes your custom assertions target, as well as on everything you can softly assert on using <span class="small-code">SoftAssertions</span> itself. Returning to our non-compiling code from above, now we can do the following:</p>
<pre><code class="java">MyProjectSoftAssertions softly = new MyProjectSoftAssertions();
softly.assertThat(frodo).hasName("frodo"); // custom assertion
softly.assertThat("ABC").endsWith("C"); // standard assertion
softly.assertAll();</code></pre>
<h3 class="page-header">Generating specific assertions with AssertJ assertions generator</h3>
<p>If you have a lot of classes and want custom assertions for all of them, it can be a lot of work !<br>
In that case, you can generate custom assertions with <a href="assertj-assertions-generator.html">AssertJ assertions generator</a> which comes with a <a href="assertj-assertions-generator-maven-plugin.html">maven plugin</a>. It takes classes or packages as input and generates the corresponding specific assertions classes (for all classes of the specified packages and subpackages).</p>
<p>The assertions generator looks for properties to know what to generate. For example, if you have a <span class="small-code">Person</span> class with an <span class="small-code">address</span> property, it will generate a <span class="small-code">hasAddress</span> assertion as part of a <span class="small-code">PersonAssert</span> assertion class.<br>
Once assertions classes are generated, you can put them in source control and enrich them with new assertions.</p>
<p>AssertJ assertions generator will also generate entry point classes for your custom assertions !</p>
</div>
</div>
</div>
<br>
<!--
<div class="container">
<footer>
<div class="row">
<div class="col-lg-12">
<p>AssertJ - Licensed under the Apache License, Version 2.0.</p>
</div>
</div>
</footer>
</div>
-->
<script src="js/jquery-1.10.2.js"></script>
<script src="js/bootstrap.js"></script>
<script src="js/modern-business.js"></script>
<script src="js/assertj.js"></script>
</body>
</html>