tag:blogger.com,1999:blog-43189219982247880492024-03-12T23:42:49.845+01:00Jean-Sébastien Franck Programming BlogJean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-4318921998224788049.post-63899814666241997022015-05-27T13:44:00.002+02:002015-05-27T13:44:45.336+02:00CasperJS UI testing best practices<a href="http://casperjs.org/">CasperJS</a> is an open source UI testing tool you can plug over PhantomJS, the famous webkit headless browser. From javascript code, you can simulate in a test all possible user interactions with your web interface. For example you can click on a link, fill and submit a form, and check your DOM elements.<br />
<br />
Just to be clear : as you interact directly with the browser, you can use CasperJS whatever is your server side technology.<br />
<br />
I started to use this tool approximatively two years ago and wanted to share some best practices I use in my UI tests.<br />
<br />
<h2>
<span style="font-size: x-large;">#1 : structure your tests with Page Object Pattern</span></h2>
<br />
This tip is definitively the most important. UI tests are essential but have a main drawback : when your DOM changes, your tests are broken.<br />
<br />
The concept of the Page Object Pattern is to have two kinds of objects in your tests : the Page objects and the scenarios :<br />
<br />
<ul>
<li>Each page object represents a real page of your web application and is an abstraction of the page DOM</li>
<li>A scenario uses several page objects to describe the user navigation and has no idea on how are structured the web pages</li>
</ul>
<br />
Applying this pattern, you will centralize in a single place the CSS or XPATH selectors of a page. If you change the page DOM, you won't need to modify several tests but only the corresponding Page object.<br />
<br />
I already covered this topic in another thread where you will find a <a href="http://jsebfranck.blogspot.fr/2014/03/page-object-pattern-with-casperjs.html">complete example of the Page Object Pattern with CasperJS</a>.<br />
<br />
<h2>
<span style="font-size: x-large;">#2 : use Resurrectio recorder</span></h2>
<br />
<a href="https://github.com/ebrehault/resurrectio">Resurrectio</a> is a useful Chrome plugin that records your actions in order to generate a CasperJS script : just activate it, do your actions on your web interface, then generate the script and your test is ready, or almost...<br />
<br />
The plugin is not perfect and as for every recorder, you will need to refactor the code (especially with Page Object Pattern). Plus, some events are forgotten by the recorder and you will need to fix some CSS selectors. BUT it is still very useful and I use it as soon as I want to create a new test on new web pages.<br />
<br />
<h2>
<span style="font-size: x-large;">#3 : empower your screenshots</span></h2>
<br />
With CasperJS, you can do at every moment in your test execution a screenshot of your web page. For example :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">casper.then(function() {
this.capture('beforeFillingTheConnectionForm.png');
});
</code></pre>
<br />
You can still give a comprehensive name to your screenshot file like in my example but when you will debug a failing test, you will quickly need some additional information. The best you can do here is to create an utility method where you can customized all screenshot filenames. From that you will do very powerful screenshots :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">exports.capture = function(screenshotName) {
casper.then(function() {
var testFile = this.test.currentSuite.file;
if (testFile.indexOf('/') != -1) {
testFile = testFile.substring(testFile.lastIndexOf('/') + 1);
}
testFile = testFile.replace('.js', '');
var testName = this.test.currentSuite.name;
var screenshotFile = 'screenshots/' + testFile + '/' + testName
+ '/' + screenshotName + '.png';
this.capture(screenshotFile);
});
};
</code></pre>
<br />
<div>
With this utility method, your screenshots are now saved in a folder screenshots/<testFilename>/<testName>. For example "screenshots/connectionScenario/Should connect then disconnect/".<br />
<br />
As far as I know, this.test.currentSuite object is not documented and I cannot assure you that CasperJS contributors will keep this variable in the future CasperJS versions.</div>
<br />
<h2>
<span style="font-size: x-large;">#4 : manage several environments</span></h2>
<br />
You surely need to execute your tests on several environments : local, tests, preproduction and so one. To manage that, you can create a config file per environment to store the environments urls or for example the user credentials. Then you set a variable telling which environment you want to use directly in CasperJS command line.<br />
<br />
For example, you can have your local configuration in the file "config/env/local.js" :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">{
url: 'http://localhost:8080',
login: 'dev',
password: 'dev'
}
</code></pre>
<br />
Your preproduction configuration in the file "config/env/preproduction.js" :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">{
url: 'http://preproduction',
login: 'admin',
password: 'admin'
}
</code></pre>
<div>
<br />
Then in a file "config/config.js", you can load the good environment file according to the command line arguments :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">var environment = casper.cli.get('env');
if (!environment) {
console.log('Usage: "casperjs test --env=<env> <scenario .js="">"');
casper.exit(1);
}
console.log('Loading the ' + environment + ' config.');
var environmentConfig = require('./env/' + environment + '.js');
module.exports = environmentConfig;
</scenario></env></code></pre>
<br />
<br />
<h2>
<span style="font-size: x-large;">#5 : use your favorite building tool</span></h2>
<br />
On a Java based web, project I started to execute CasperJS directly from Maven thanks to <a href="http://www.yegor256.com/2014/06/21/casperjs-with-maven.html">this thread</a>. It is interesting because Maven directly handles PhantomJS and CasperJS installation. Quite appreciable for your development workstations and for your favorite continuous delivery tool.<br />
<br />
I haven't tried yet Grunt or Gulp implementations but you will easily find that on Google ;-)<br />
<br />
<h2>
<span style="font-size: x-large;">Conclusion</span></h2>
<br />
I hope you enjoyed these tips. Don't hesitate to give me your opinion on this thread or to share your own best practices with me.<br />
<br />
Happy UI testing !<br />
<br />
<br />
<br /></div>
Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-89748953496342949442015-01-28T09:59:00.000+01:002015-01-28T10:01:04.487+01:00Your webapp in the cloud with AWS Elastic Beanstalk<a href="http://3.bp.blogspot.com/-Jv73eKiuTEk/U90HUgryNGI/AAAAAAAABQc/N7SsbrtfB3o/s1600/logo-xebia.jpg" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://3.bp.blogspot.com/-Jv73eKiuTEk/U90HUgryNGI/AAAAAAAABQc/N7SsbrtfB3o/s1600/logo-xebia.jpg" height="200" width="200" /></a><i><a href="http://blog.xebia.fr/2015/01/27/votre-application-web-dans-le-cloud-avec-aws-elastic-beanstalk/" target="_blank">Xebia article I wrote (in french), translation coming soon</a></i>Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-43502031959145132602015-01-24T15:10:00.001+01:002015-01-24T15:12:00.165+01:00Ten stories of a Node.js webapp in production in the cloud - Node.js meetup Paris<div class="separator" style="clear: both; text-align: center;">
<object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="https://ytimg.googleusercontent.com/vi/m6n7Qe_26Cs/0.jpg" height="266" width="320"><param name="movie" value="https://youtube.googleapis.com/v/m6n7Qe_26Cs&source=uds" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="https://youtube.googleapis.com/v/m6n7Qe_26Cs&source=uds" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div>
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-4652189800232193002015-01-12T11:27:00.001+01:002015-01-12T11:28:33.438+01:00Web performance with Sitespeed.io<a href="http://3.bp.blogspot.com/-Jv73eKiuTEk/U90HUgryNGI/AAAAAAAABQc/N7SsbrtfB3o/s1600/logo-xebia.jpg" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://3.bp.blogspot.com/-Jv73eKiuTEk/U90HUgryNGI/AAAAAAAABQc/N7SsbrtfB3o/s1600/logo-xebia.jpg" height="200" width="200" /></a><i><a href="http://blog.xebia.fr/2015/01/12/mesurer-la-performance-de-vos-pages-web-avec-sitespeed-io/" target="_blank">Xebia article I wrote (in french), translation coming soon</a></i>Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-41897092961758264812014-11-11T17:35:00.000+01:002015-01-24T15:08:51.354+01:00Ten stories of a Node.js webapp in production in the cloud - Xebia Knowledge Exchange Paris<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/L8f5zXAtW-4?feature=player_embedded' frameborder='0'></iframe></div>
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-19548160111230291022014-10-22T22:36:00.003+02:002014-10-22T22:37:14.014+02:00Node.js : Create a REST API with Strongloop tools<a href="http://3.bp.blogspot.com/-Jv73eKiuTEk/U90HUgryNGI/AAAAAAAABQc/N7SsbrtfB3o/s1600/logo-xebia.jpg" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://3.bp.blogspot.com/-Jv73eKiuTEk/U90HUgryNGI/AAAAAAAABQc/N7SsbrtfB3o/s1600/logo-xebia.jpg" height="200" width="200" /></a><i><a href="http://blog.xebia.fr/2014/10/14/node-js-creer-une-api-rest-avec-les-outils-de-strongloop/" target="_blank">Xebia article I wrote (in french), translation coming soon</a></i>Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-11245601038697810362014-08-02T17:55:00.000+02:002014-08-04T20:44:32.883+02:00Develop and test a Node.js HTTP client<div style="text-align: left;">
</div>
Call an external HTTP service is a very common task when we develop a Node.js application. In production, an external service can be instable and it is essential that this instability does not broke your application. Whatever the project or the external service, cases to take into account are always the same.<br />
<br />
From a simple use case, we will see in this article what are the different steps to follow in order to create a robust HTTP client in Node.js.<br />
<br />
<h2>
<span style="font-size: x-large;">Our example</span></h2>
Our goal is to create a method countEmployees returning the result of the following HTTP request :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">GET http://localhost:3000/employees/count
{"count":1200}
</code></pre>
<br />
<h2>
<span style="font-size: x-large;">Step 1 : nominal case</span></h2>
<br />
In this step, we will write a first implementation of our client and test the case where the external service responds correctly.<br />
<br />
To begin, using <a href="https://github.com/visionmedia/mocha">mocha</a>, let's create a test which verifies that the value returned by the client is directly the value returned by the external service.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">describe('employees count service', function() {
it('should return 1200', function() {
client.countEmployees().then(function(count) {
count.should.equal(1200);
}).done();
});
});
</code></pre>
<br />
Then, write a first implementation of the countEmployees method :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">var Q = require('Q'),
request = require('request'),
config = require('./config');
exports.countEmployees = function() {
var deferred = Q.defer();
var options = {
url: config.employeeCountUrl,
json: true
};
request(options, function (error, response, body) {
var employeesCount = body.count;
deferred.resolve(employeesCount);
});
return deferred.promise;
};
</code></pre>
<br />
We use in this implementation two modules : request for the HTTP requests and Q for the promises.<br />
<br />
Here our test do a real HTTP call to the external service. To make the test pass, the HTTP service must be available and always return the same value. So it becomes essential to simulate this service. To do that, we will use <a href="https://github.com/pgte/nock">nock</a> module which allows to mock HTTP access in Node.js.<br />
<br />
Starting with nock is easy thanks to its recorder. Firstly let's add the following instruction in our test :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">beforeEach(function() {
nock.recorder.rec();
});
</code></pre>
<br />
During the test execution, we discover these lines in the console :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><<<<<<-- cut here -->>>>>>
nock('http://localhost:3000')
.get('/employees/count')
.reply(200, {"count":1200}, { 'x-powered-by': 'Express', 'content-type': 'application/json;
charset=utf-8', 'content-length': '14', etag: 'W/"e-2043423703"', date: 'Wed, 09 Jul 2014 20:59:20 GMT',
connection: 'keep-alive' });
<<<<<<-- cut here -->>>>>>
</code></pre>
<br />
You just have to copy past these lines in your test to simulate the execution of the request http://localhost:3000/employees/count. Don't hesitate to clean what you don't need, like for example the response header.<br />
<br />
Another useful nock instruction is this one : nock.disableNetConnect() which allows to forbid all HTTP access. Our test becomes :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">describe('employees count service', function() {
beforeEach(function() {
nock.disableNetConnect();
});
it('should return employees count', function() {
nock('http://localhost:3000')
.get('/employees/count')
.reply(200, {count:1986});
client.countEmployees().then(function(count) {
count.should.equal(1986);
}).done();
});
});
</code></pre>
<br />
And now, when the client executes the request, it is not the service which answers but nock. Moreover, we have the guarantee that we don't do extra HTTP requests. Now, shutdown the HTTP service and relaunch the test : it passes !<br />
<br />
Our first goal is reached : we have a first implementation of our client and a test covering the nominal case.<br />
<br />
<h2>
<span style="font-size: x-large;">Step 2 : service in error</span></h2>
<br />
Using nock, it is now easy to test the error cases. In this second step, we want to handle the case where the service returns an HTTP error. Let's add a new test and simulate the case where the service returns an error 500.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">it('should return an error if http service is in error', function(done) {
nock('http://localhost:3000')
.get('/employees/count')
.reply(500, {});
client.countEmployees().then(function(count) {
done(new Error('method should return an error'));
}).catch(function() {
done();
});
});
</code></pre>
<br />
The test doesn't pass. Indeed, the client doesn't return an error but a null result. Don't panic, we can easily handle this case and return an error if the http status code is not in the 200 range :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">if (response.statusCode >= 300) {
deferred.reject(new Error('Service has an invalid status code : ' + response.statusCode));
}
</code></pre>
<br />
Step 2 goal is reached : our client can now handle HTTP errors.<br />
<br />
<h2>
<span style="font-size: x-large;">Step 3 : service returns unexpected data</span></h2>
<br />
In this third step, we want to check that the client works correctly if the service returns unexpected data. In a new test, we will simulate that the service returns no count field but another field. With nock again, it is easy :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">whenEmployeesCountIsCalled().reply(200, {nb:1986});
</code></pre>
<br />
Please note that we factorized the nock call with the following method :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">var whenEmployeesCountIsCalled = function() {
return nock('http://localhost:3000')
.get('/employees/count');
};
</code></pre>
<br />
The test fails. To fix it, you can test that the expected field is in the HTTP request :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">if (!employeesCount) {
deferred.reject(new Error('Service did not return employees count'));
}
</code></pre>
<br />
Step 3 goal is reached : we know how to handle unexpected data.<br />
<br />
<h2>
<span style="font-size: x-large;">Step 4 : service is slow</span></h2>
<br />
In this step, we want to test the case where the external service is too slow. We can also do that with nock :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">whenEmployeesCountIsCalled()
.delayConnection(1500)
.reply(200, {count:1986});
</code></pre>
<br />
DelayConnection instruction allows to delay the HTTP answer of 1500 ms. Here we want that our service interrupts the connection after, for example 500 ms. With request module, you can configure a timeout :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">var options = {
url: config.employeeCountUrl,
json: true,
timeout: 500
};
</code></pre>
<br />
Then you have to test if the error object is defined :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">if (error) {
deferred.reject(error);
return;
}
</code></pre>
<br />
The test passes. Step 4 is finished : our client is protected from slow access.<br />
<br />
<h2>
<span style="font-size: x-large;">Step 5 : service is unavailable</span></h2>
<br />
In this last step, we want to check our client behavior with an unavailable service. Current version of nock cannot help to test this case. However we just need to do a real HTTP access on an unexisting host :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">it('should return an error if service is unavailable', function(done) {
client = rewire('../client/employees.client');
client.__set__('config', {
configEmployeeCountUrl: 'http://doesnotexist.localhost:3000/employees/count'
});
client.countEmployees().then(function() {
done(new Error('method should return an error'));
}).catch(function() {
done();
});
});
</code></pre>
<br />
Here we use <a href="https://github.com/jhnns/rewire">rewire</a> module to override the config object and give an unexisting url. The test passes. Indeed the fix from step 4 allows also to handle this error. Our goal is reached, we finalized the implementation of our HTTP client !<br />
<br />
<h2>
<span style="font-size: x-large;">Conclusion </span></h2>
Unavailable, slow, broken... production hazards are large and it is important that your application stays stable in these cases. Thanks to nock, we can easily reproduced these errors and build a robust code.<br />
<br />
We covered only a few features of nock. To go further, don't hesitate to read the full documentation here : <a href="https://github.com/pgte/nock">https://github.com/pgte/nock</a>.<br />
<br />
To finish, you will find the complete code on the following github respository : <a href="https://github.com/jsebfranck/node-http-example">https://github.com/jsebfranck/node-http-example</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-Jv73eKiuTEk/U90HUgryNGI/AAAAAAAABQc/N7SsbrtfB3o/s1600/logo-xebia.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-Jv73eKiuTEk/U90HUgryNGI/AAAAAAAABQc/N7SsbrtfB3o/s1600/logo-xebia.jpg" height="200" width="200" /></a></div>
<br />
<br />
<br />
<br />
<br />
<i><a href="http://blog.xebia.fr/2014/08/04/developper-et-tester-un-client-http-en-node-js/" target="_blank">Translated from a Xebia article I wrote (in french)</a></i>Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-60662366520094220102014-03-09T22:46:00.000+01:002015-06-02T21:46:18.842+02:00Page Object pattern with CasperJSIn this article I will quickly introduce an UI test framework, <a href="http://casperjs.org/">CasperJS</a>, and how we can improve UI tests maintainability using the Page Object Pattern. The goal is not to cover all CasperJS features but to show how to write maintainable tests.<br />
<div>
<br /></div>
CasperJS is an open source tool used to test your web application user interface. From javascript code, you can simulate in a test all possible users interactions with your interface. For example you can click on a link, fill and submit a form, and finally check your DOM elements.<br />
<br />
To start, let's see a concrete example of a CasperJS test and after we will see how to refactor the written tests using the Page Object pattern.<br />
<br />
<h2>
<span style="font-size: x-large;">Our example</span></h2>
We will test three pages of the spring travel application :<br />
<ul>
<li>the login page</li>
<li>the hotels search page & bookings listing</li>
<li>and the hotels search result page</li>
</ul>
<ul>
</ul>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-QK0EGIUgPww/Uxw9WqiE7gI/AAAAAAAABME/Zjhcn6UfSdE/s1600/Capture+d%E2%80%99e%CC%81cran+2014-03-09+a%CC%80+11.08.56.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-QK0EGIUgPww/Uxw9WqiE7gI/AAAAAAAABME/Zjhcn6UfSdE/s1600/Capture+d%E2%80%99e%CC%81cran+2014-03-09+a%CC%80+11.08.56.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Login page</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-wpNb745-bUo/Uxw9ZcmjtTI/AAAAAAAABMM/BJ_N9rSx0Jg/s1600/Capture+d%E2%80%99e%CC%81cran+2014-03-09+a%CC%80+11.09.19.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="343" src="http://2.bp.blogspot.com/-wpNb745-bUo/Uxw9ZcmjtTI/AAAAAAAABMM/BJ_N9rSx0Jg/s1600/Capture+d%E2%80%99e%CC%81cran+2014-03-09+a%CC%80+11.09.19.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Hotels search page & bookings listing</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-Yi1fTzBwGLc/Uxw9Zp9eJBI/AAAAAAAABMQ/xZ8YD0m0wLk/s1600/Capture+d%E2%80%99e%CC%81cran+2014-03-09+a%CC%80+11.09.32.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="188" src="http://1.bp.blogspot.com/-Yi1fTzBwGLc/Uxw9Zp9eJBI/AAAAAAAABMQ/xZ8YD0m0wLk/s1600/Capture+d%E2%80%99e%CC%81cran+2014-03-09+a%CC%80+11.09.32.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Hotels search result page</td></tr>
</tbody></table>
<div>
<br /></div>
For our first CasperJS test, we want to cover the following scenario :<br />
<ul>
<li>the user fills and submits the login form</li>
<li>the user arrives on the bookings listing</li>
<li>the user can see his last bookings</li>
</ul>
<div>
Here is the CasperJS test :</div>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">casper.test.begin('When I connect myself I should see my bookings', function (test) {
casper.start(casper.cli.options.baseUrl + '/login');
casper.then(function () {
test.assertExists('form[name="f"]', 'Is on login page');
});
casper.then(function () {
this.fill('form[name="f"]', {
'j_username': 'scott',
'j_password': 'rochester'
}, false);
});
casper.then(function () {
this.click('form[name="f"] button[type="submit"]', 'Login submit button clicked');
});
casper.then(function () {
test.assertUrlMatch('hotels/search', 'Is on search page');
test.assertTextExists('Current Hotel Bookings', 'bookings title are displayed');
test.assertExists('#bookings > table > tbody > tr', 'bookings are displayed');
});
casper.run(function () {
test.done();
});
});
</code></pre>
<br />
As you can see this test is very fluent and easily readable. Some explanations :<br />
<ul>
<li><i>casper.start</i> starts the scenario on a given url</li>
<li><i>casper.then</i> sections describe a specific user action or some assertions.</li>
<li><i>fill</i> and <i>click</i> methods allow to simulate user actions</li>
<li><i>assertExists</i>, <i>assertUrlMatch</i> and <i>assertTestExists</i> allow to check the DOM content</li>
<li><i>casper.cli.options.baseUrl</i> allows to get a custom parameter passed on the casper js command line</li>
</ul>
Now let's cover a little bit more complex scenario in a new CasperJS test :<br />
<ul>
<li>the user fills and submits the login form</li>
<li>the user arrives on the hotels search page</li>
<li>the user fills and submits the hotels search page</li>
<li>the user can see several hotels in Atlanta</li>
</ul>
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">casper.test.begin('When I connect myself and search hotels in Atlanta
Then should find three hotels', function (test) {
casper.start(casper.cli.options.baseUrl + '/login');
casper.then(function () {
test.assertExists('form[name="f"]', 'Is on login page');
});
casper.then(function () {
this.fill('form[name="f"]', {
'j_username': 'scott',
'j_password': 'rochester'
}, false);
});
casper.then(function () {
this.click('form[name="f"] button[type="submit"]', 'Login submit button clicked');
});
casper.then(function () {
test.assertUrlMatch('hotels/search', 'Is on search page');
});
casper.then(function () {
this.fill('form[id="searchCriteria"]', {
'searchString': 'Atlanta'
}, false);
});
casper.then(function () {
this.click('form[id="searchCriteria"] button[type="submit"]');
});
casper.then(function () {
test.assertUrlMatch('hotels?searchString=Atlanta', 'Is on search result page');
test.assertElementCount('#hotelResults > table > tbody > tr', 3, '3 hotels have been found');
});
casper.run(function () {
test.done();
});
});
</code></pre>
<br />
Again the test is fluent and readable. But a lot of code has just been copy/paste and we have now several duplicated lines of code.<br />
<br />
How can we factorize that? By creating some utils methods? Not exactly, it is here that comes the famous Page Object pattern!<br />
<br />
<h2>
<span style="font-size: x-large;">Page Object pattern</span></h2>
Page Object pattern is described on <a href="http://martinfowler.com/bliki/PageObject.html">Martin Fowler website</a>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-qKz6K5MUPlE/Uxx-6OlYP6I/AAAAAAAABMk/nwyBeSMYgJo/s1600/pageObject.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="352" src="http://1.bp.blogspot.com/-qKz6K5MUPlE/Uxx-6OlYP6I/AAAAAAAABMk/nwyBeSMYgJo/s1600/pageObject.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Page Object pattern by Martin Fowler</td></tr>
</tbody></table>
<br />
<br />
<u>The main ideas are :</u><br />
<br />
The test must not manipulate directly the page UI elements. This manipulation must be done within a Page Object which represents an UI page or an UI page fragment. The Page Object makes a complete abstraction of the underlying UI and it becomes an API where it is possible to easily find and manipulate the page data.<br />
<br />
This encapsulation has two benefits : the test logic is about user intentions and not about UI details, so it is easier to understand. Plus, if the UI is modified, this will affect only the Page Objects and not the tests.<br />
<br />
Asynchronism behavior of the pages must also be hidden by the Page Object. It is a specific behavior of your UI and you don't want to make it appear in your tests.<br />
<br />
For a single page, you can have several Page Objects if there are several significant elements on the page. For example you can have a Page Object for the header and one for the body.<br />
<br />
Assertions responsibility can be in the Page Object or in the test. In the Page Object it helps avoid duplication of assertions in the tests, but the Page Object responsibility becomes more complicated as it is responsible to give access to the page data, plus to have the assertion logic.<br />
<br />
This pattern can be apply for any UI technologies : HTML pages, java swing interfaces or others UI.<br />
<div>
<br /></div>
Now, let's use the Page Object pattern for our CasperJS tests.<br />
<br />
<h2>
<span style="font-size: x-large;">CasperJS with Page Object pattern</span></h2>
In our test, we navigate through three pages, so we will create three Page Objects : LoginPage, SearchPage and SearchResultPage.<br />
<br />
Our first page is the login page. Here we must be able to start the scenario on this page, to check that the page is correct, and to fill and submit the login form. We will do that with four methods : startOnLoginPage, checkPage, fillForm and submitForm. All of these methods are created in a LoginPage object in a LoginPage.js file :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">function LoginPage() {
this.startOnLoginPage = function () {
casper.echo("base url is : " + casper.cli.options.baseUrl);
casper.start(casper.cli.options.baseUrl + '/login');
};
this.checkPage = function () {
casper.then(function () {
casper.test.assertUrlMatch('login', 'Is on login page');
casper.test.assertExists('form[name="f"]', 'Login page form has been found');
});
};
this.fillForm = function (username, password) {
casper.then(function () {
this.fill('form[name="f"]', {
'j_username': username,
'j_password': password
}, false);
});
};
this.submitForm = function () {
casper.then(function () {
this.click('form[name="f"] button[type="submit"]', 'Login submit button clicked');
});
};
}
</code></pre>
<br />
Now we need a page for the search. We need to check the page, to check that the user bookings are displayed, and to fill and submit the search form. We will do that on a SearchPage object in the file SearchPage.js :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">function SearchPage() {
this.checkPage = function () {
casper.then(function () {
casper.test.assertUrlMatch('hotels/search', 'Is on search page');
});
};
this.checkThatBookingsAreDisplayed = function() {
casper.then(function () {
casper.test.assertTextExists('Current Hotel Bookings', 'bookings title are displayed');
casper.test.assertExists('#bookings > table > tbody > tr', 'bookings are displayed');
});
};
this.fillSearchForm = function(searchTerms) {
casper.then(function () {
this.fill('form[id="searchCriteria"]', {
'searchString': searchTerms
}, false);
});
};
this.submitSearchForm = function() {
casper.then(function () {
this.click('form[id="searchCriteria"] button[type="submit"]');
});
};
}
</code></pre>
<br />
Finally we need a Page Object for our SearchResultPage. Here we just want to check the page and to check that the results are correctly displayed :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">function SearchResultPage() {
this.checkPage = function () {
casper.then(function () {
casper.test.assertUrlMatch('hotels?searchString=', 'Is on search result page');
});
};
this.checkThatResultsAreDisplayed = function(expectedCount) {
casper.then(function () {
casper.test.assertElementCount('#hotelResults > table > tbody > tr', expectedCount, expectedCount + ' hotels have been found');
});
};
}
</code></pre>
<br />
Now we can use these three Page Objects in our test :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">phantom.page.injectJs('LoginPage.js');
phantom.page.injectJs('SearchPage.js');
phantom.page.injectJs('SearchResultPage.js');
var loginPage = new LoginPage();
var searchPage = new SearchPage();
var searchResultPage = new SearchResultPage();
casper.test.begin('When I connect myself I should see my bookings', function (test) {
loginPage.startOnLoginPage();
loginPage.checkPage();
loginPage.fillForm('scott', 'rochester');
loginPage.submitForm();
searchPage.checkPage();
searchPage.checkThatBookingsAreDisplayed();
casper.run(function () {
test.done();
});
});
casper.test.begin('When I connect myself and search hotels in Atlanta
Then should find three hotels', function (test) {
loginPage.startOnLoginPage();
loginPage.checkPage();
loginPage.fillForm('scott', 'rochester');
loginPage.submitForm();
searchPage.checkPage();
searchPage.fillSearchForm('Atlanta');
searchPage.submitSearchForm();
searchResultPage.checkPage();
searchResultPage.checkThatResultsAreDisplayed(3);
casper.run(function () {
test.done();
});
});
</code></pre>
<br />
Our two tests are now more readable and there is a complete abstraction of UI elements. If you modify your HTML code, you will easily identify which page to modify and you won't have impacts on your tests.<br />
<br />
<h2>
<span style="font-size: x-large;">Variant n°1 : use four Page Objects</span></h2>
The search page provides the search form and the bookings listing. I choose to modelize that in a single Page Object. But another option is to create two Page Object : one for the search and one for the listing.
The BookingListingPage.js is now :
<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">function BookingListingPage() {
this.checkPage = function () {
casper.then(function () {
casper.test.assertUrlMatch('hotels/search', 'Is on booking listing page');
});
};
this.checkThatBookingsAreDisplayed = function() {
casper.then(function () {
casper.test.assertTextExists('Current Hotel Bookings', 'bookings title are displayed');
casper.test.assertExists('#bookings > table > tbody > tr', 'bookings are displayed');
});
};
}
</code></pre>
<br />
And our first test becomes :
<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">casper.test.begin('When I connect myself I should see my bookings', function (test) {
loginPage.startOnLoginPage();
loginPage.checkPage();
loginPage.fillForm('scott', 'rochester');
loginPage.submitForm();
bookingListingPage.checkPage();
bookingListingPage.checkThatBookingsAreDisplayed();
casper.run(function () {
test.done();
});
});
</code></pre>
<br />
<h2>
<span style="font-size: x-large;">Variant n°2 : keep assertions in tests</span></h2>
I choose to write the assertions directly in the Page Objects. Thess objects have then two responsibilities : give an access to the page data and provide assertions. Martin Fowler recommends to distinguish these responsitilibies and to keep assertions in the test. In that case the Page Object provides only an accessor to the page element and it is the test responsibility to check its content.
For example for the SearchResultPage Object, the method :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">// test
searchResultPage.checkThatResultsAreDisplayed(3);
// page object
this.checkThatResultsAreDisplayed = function(expectedCount) {
casper.then(function () {
casper.test.assertElementCount('#hotelResults > table > tbody > tr', expectedCount, expectedCount + ' hotels have been found');
});
};
</code></pre>
<br />
Becomes :
<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">// test
casper.test.assertEquals(searchResultPage.getResultsCount(), 3, '3 hotels have been found');
// page object
this.getResultsCount = function() {
return casper.evaluate(function() {
return __utils__.findAll('#hotelResults > table > tbody > tr').length;
});
};
</code></pre>
<br />
<br />
<h2>
<span style="font-size: x-large;">Conclusion</span></h2>
When you code your features, you don’t hesitate to separate the different layers in different objects. It is exactly what the Page Object pattern recommend to do : separate the test logic from the UI layer. In this article I applied it to CasperJS tests but this pattern is also relevant with other tools like for example Selenium, ZombieJS or even Gatling.
<br />
<br />
Please find the complete code on <a href="https://github.com/jsebfranck/test-samples">my github repository about tests</a>.<br />
Please also find my CasperJS best practices <a href="http://jsebfranck.blogspot.fr/2015/05/casperjs-ui-testing-best-practices.html">here</a>.<br />
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com7tag:blogger.com,1999:blog-4318921998224788049.post-84752674197062240142014-01-17T09:26:00.005+01:002014-01-17T09:26:47.632+01:00Unit tests and integration tests coverage in sonar with gradleI recently needed to configure a multi-module gradle project in order to generate unit test, integration test and overall coverage indicators in sonar. As it wasn't so obvious I thought it would be a good idea to share my example.<br />
<br />
The goal is to generate the following report in sonar :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-jiEN5vxENvo/UthYXKKLNYI/AAAAAAAAAIw/sl5bWgmGJuw/s1600/Capture+d%E2%80%99e%CC%81cran+2014-01-16+a%CC%80+23.07.53.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-jiEN5vxENvo/UthYXKKLNYI/AAAAAAAAAIw/sl5bWgmGJuw/s1600/Capture+d%E2%80%99e%CC%81cran+2014-01-16+a%CC%80+23.07.53.png" /></a></div>
<br />
<h2>
<span style="font-size: x-large;">Complete gradle file</span></h2>
<br />
So here is my complete build.gradle file :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">apply plugin: 'sonar-runner'
allprojects {
apply plugin: 'java'
apply plugin: 'jacoco'
sourceSets {
integtest {
java {
compileClasspath += main.output
runtimeClasspath += main.output
srcDir 'src/integtest/java'
}
}
}
configurations {
integtestCompile.extendsFrom testCompile
}
task "integtest"(type: Test, dependsOn: integtestClasses) {
testClassesDir = sourceSets.integtest.output.classesDir
classpath = sourceSets.integtest.runtimeClasspath
jacoco {
destinationFile = file("$buildDir/jacoco/jacocoIntegTest.exec")
}
}
test {
jacoco {
destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
}
}
sonarRunner {
sonarProperties {
property "sonar.jacoco.reportPath", "$buildDir/jacoco/jacocoTest.exec"
property "sonar.jacoco.itReportPath", "$buildDir/jacoco/jacocoIntegTest.exec"
}
}
}
sonarRunner {
sonarProperties {
property "sonar.projectName", "test-samples"
property "sonar.projectKey", "jsebfranck.samples"
}
}
</code></pre>
<br />
<h2>
<span style="font-size: x-large;">Explanations</span></h2>
I used Jacoco to generate my tests coverage indicators :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> apply plugin: 'jacoco'
</code></pre>
<br />
The following lines allow to configure my integration tests. To distinguish these tests from the unit tests, I moved them in a specific source folder : "src/integtest/java". Please note that with gradle, the default test folder is "src/test/java" and I use it for my unit tests.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> sourceSets {
integtest {
java {
compileClasspath += main.output
runtimeClasspath += main.output
srcDir 'src/integtest/java'
}
}
}
configurations {
integtestCompile.extendsFrom testCompile
}
task "integtest"(type: Test, dependsOn: integtestClasses) {
testClassesDir = sourceSets.integtest.output.classesDir
classpath = sourceSets.integtest.runtimeClasspath
}
</code></pre>
<br />
I configured Jacoco to generate the tests coverage data in a specific directory : one for the unit tests and for the integration tests :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> task "integtest"(type: Test, dependsOn: integtestClasses) {
testClassesDir = sourceSets.integtest.output.classesDir
classpath = sourceSets.integtest.runtimeClasspath
jacoco {
destinationFile = file("$buildDir/jacoco/jacocoIntegTest.exec")
}
}
test {
jacoco {
destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
}
}
</code></pre>
<br />
Then for each module, I configured sonar to use generated Jacoco data :<br />
<ul>
<li>reportPath property allows to configure the unit tests coverage</li>
<li>itReportPath property allows to configure the integration tests coverage</li>
</ul>
<div>
<pre style="background-color: #f0f0f0; border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 646.46875px;"><code style="word-wrap: normal;">allprojects {
sonarRunner {
sonarProperties {
property "sonar.jacoco.reportPath", "$buildDir/jacoco/jacocoTest.exec"
property "sonar.jacoco.itReportPath", "$buildDir/jacoco/jacocoIntegTest.exec"
}
}
}</code></pre>
</div>
<div>
<br />
Finally I configured my sonar instance. Here I use the default parameters so the report is generated on my localhost sonar installation and its h2 database :<br />
<br /></div>
<div>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">sonarRunner {
sonarProperties {
property "sonar.projectName", "test-samples"
property "sonar.projectKey", "jsebfranck.samples"
}
}</code></pre>
</div>
<span style="font-size: x-large;"><br /></span>
<br />
<h2>
<span style="font-size: x-large;">Source code</span></h2>
<div>
Please find the complete source code on <a href="https://github.com/jsebfranck/test-samples">my github repository about tests</a>.</div>
<div>
<span style="font-size: x-large;"><br /></span></div>
<br />
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com4tag:blogger.com,1999:blog-4318921998224788049.post-41769944878615567542014-01-05T22:16:00.000+01:002014-01-05T22:16:26.858+01:00Mockito in the trenchesI used <a href="http://easymock.org/">EasyMock</a> framework for three years, before starting to use <a href="https://code.google.com/p/mockito/">Mockito</a> two years ago and I'm now really happy with this framework.<br />
<br />
However, it provides some features I never used, and for some features it provides several ways to write your tests. For example you have two ways to create your mocks and two ways to write your expectations.<br />
<br />
So, in this article I will present every feature I use, and how I choose to write my Mockito tests.<br />
<br />
<h2>
<span style="font-size: x-large;">Our example</span></h2>
Let's start with a simple example, then we will go deeper into the framework.<br />
<br />
The class we want to test is a class managing accounts, AccountService, with a single dependency, its repository : AccountRepository. For the moment, AccountService has only one method :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">public class AccountService {
@Inject
private AccountRepository accountRepository;
public Account getAccountByLogin(String login) {
try {
return accountRepository.findAccount(login);
} catch (EntityNotFoundException enfe) {
return null;
}
}
}
</code></pre>
<br />
How can we test that with Mockito?<br />
<br />
<h2>
<span style="font-size: x-large;">Mocks creation</span></h2>
<br />
The first thing you have to do is to create your class of test and create your mocks. I recommend to use the Mockito annotations and the Junit runner as it is more elegant and concise :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">@RunWith(MockitoJUnitRunner.class)
public class AccountServiceTest {
@InjectMocks
private AccountService accountService;
@Mock
private AccountRepository accountRepository;
}
</code></pre>
<br />
As you can see, you don't need a @Before method and you don't have to instantiate your objects. All is done automatically by the framework. To inject the mocks in the class to test, Mockito tries to do that using firstly the object constructor, then the object setters or finally the object properties.<br />
<br />
<h2>
<span style="font-size: x-large;">doReturn and verify</span></h2>
<br />
Now let's test the method getAccountByLogin and the case where an account already exists.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> private static final String LOGIN = "login";
private static final String PASSWORD = "password";
@Test
public void getAccountByLogin_withExistingAccount_shouldReturnTheAccount()
throws Exception {
// Given
Account existingAccount = new Account(LOGIN, PASSWORD);
doReturn(existingAccount).when(accountRepository).findAccount(LOGIN);
// When
Account result = accountService.getAccountByLogin(LOGIN);
// Then
assertEquals(existingAccount, result);
verify(accountRepository).findAccount(LOGIN);
verifyNoMoreInteractions(accountRepository);
}
</code></pre>
<br />
Explanations :<br />
<br />
<ul>
<li>doReturn allows to override the behavior of the AccountRepository.findAccount method. Here we return an Account object to simulate the case that an account already exists</li>
<li>verify(accountRepository).findAccount allows to check that this method has been called</li>
<li>verifyNoMoreInteractions method allows to be sure that we didn't call another method on this mock. It is useful in that case because you test that you don't do a useless call on your repository</li>
</ul>
<br />
<h2>
<span style="font-size: x-large;">doThrow</span></h2>
Now let's test the case where the account doesn't exist. In that case the repository throws an EntityNotFoundException :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> @Test
public void getAccountByLogin_withUnexistingAccount_shouldReturnNull() throws Exception {
// Given
doThrow(new EntityNotFoundException()).when(accountRepository).findAccount(LOGIN);
// When
Account result = accountService.getAccountByLogin(LOGIN);
// Then
assertNull(result);
}
</code></pre>
<br />
As we used doReturn before, here we use doThrow method. Easy!<br />
<br />
<h2>
<span style="font-size: x-large;">doReturn vs thenReturn, doThrow vs thenThrow</span></h2>
With mockito the following instructions are similar :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> // either :
doReturn(null).when(accountService).getAccountByLogin(LOGIN);
// or :
when(accountService.getAccountByLogin(LOGIN)).thenReturn(null);
// either :
doThrow(new EntityNotFoundException()).when(accountRepository).findAccount(LOGIN);
// or :
when(accountRepository.findAccount(LOGIN)).thenThrow(new EntityNotFoundException());
</code></pre>
<br />
However, in the case you want to test a void method, you can write :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> doThrow(new EntityNotFoundException()).when(accountRepository).deleteAccount(LOGIN);
</code></pre>
<br />
But you cannot write the following instruction because it doesn't compile :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> when(accountRepository.deleteAccount(LOGIN)).thenThrow(new EntityNotFoundException());
</code></pre>
<br />
So I recommend to always use the do* methods in order to keep your tests coherent.<br />
<br />
<h2>
<span style="font-size: x-large;">Object.equals and argument captor</span></h2>
<br />
Now let's add another method which create an account :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public void createAccount(String login, String password) throws ServiceException {
Account account = new Account(login, password);
accountRepository.createAccount(account);
}
</code></pre>
<br />
It seems very easy to write this test. Let's do a first try :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> @Test
public void createAccount_nominalCase_shouldCreateTheAccount() throws Exception {
// When
accountService.createAccount(LOGIN, PASSWORD);
// Then
verify(accountRepository).createAccount(new Account(LOGIN, PASSWORD));
verifyNoMoreInteractions(accountRepository);
}
</code></pre>
<br />
But unfortunately the test fails! Why? Because the verify instruction checks that the parameters passed in the createAccount method are the same in the test that in the tested code. However, in that case, the Account object has no equals method.<br />
<br />
You may think to add an equals method in the Account object but if you do that only for your unit test, it is really a shame. Hopefully Mockito provides a cleaner way to test this case : the argument captor.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> @Test
public void createAccount_nominalCase_shouldCreateTheAccount() throws Exception {
// When
accountService.createAccount(LOGIN, PASSWORD);
// Then
assertThatAccountAsBeenCreated();
verifyNoMoreInteractions(accountRepository);
}
private void assertThatAccountAsBeenCreated() {
ArgumentCaptor<account> argument = ArgumentCaptor.forClass(Account.class);
verify(accountRepository).createAccount(argument.capture());
Account createdAccount = argument.getValue();
assertEquals(LOGIN, createdAccount.getLogin());
assertEquals(PASSWORD, createdAccount.getPassword());
}
</account></code></pre>
<br />
With this technic, you can retrieve the object passed in the tested code, then do every assertion you want. Here we just want to be sure that the login and the password are correct.<br />
<br />
<h2>
<span style="font-size: x-large;">Partial mocking</span></h2>
Now let's add three lines of code in our createAccount method to check that the account doesn't exist before we create it. To do that, we use the existing method getAccountByLogin :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> public void createAccount(String login, String password) throws ServiceException {
if (getAccountByLogin(login) != null) {
throw new ServiceException(String.format("The account %s already exists", login));
}
Account account = new Account(login, password);
accountRepository.createAccount(account);
}
</code></pre>
<br />
As you have already tested the getAccountByLogin, you don't want to test it again because our tests could become unmaintainable.<br />
<br />
So here you can use the partial mocking. The idea is to mock the call to the getAccountByLogin method within the class you are testing. With Mockito (unlike other mock frameworks) it is very easy to do that. You just have to override your method behavior like if you were using a mock :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> @Test
public void createAccount_nominalCase_shouldCreateTheAccount() throws Exception {
// Given
doReturn(null).when(accountService).getAccountByLogin(LOGIN);
// When
accountService.createAccount(LOGIN, PASSWORD);
// Then
assertThatAccountAsBeenCreated();
verifyNoMoreInteractions(accountRepository);
verify(accountService).getAccountByLogin(LOGIN);
}
</code></pre>
<br />
Then you just have to add the @Spy annotation on the declaration of your tested object :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> @InjectMocks
@Spy
private AccountService accountService;
</code></pre>
<br />
And it works!<br />
<br />
<h2>
<span style="font-size: x-large;">Conclusion</span></h2>
I think I covered every feature of Mockito I use every day. Don't hesitate to ping me if I forgot something important. Anyway, I hope this tutorial helped you to have a better understanding of this great framework!<br />
<br />
The whole code, with more cases and tests, is available on <a href="https://github.com/jsebfranck/test-samples">my github account</a>.<br />
<br />
Also, don't hesitate to consult <a href="http://jsebfranck.blogspot.fr/2013/06/tips-to-write-maintainable-unit-tests.html">my recommendations to write maintainable unit tests</a>.<br />
<br />
Cheers!<br />
<br />
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com3tag:blogger.com,1999:blog-4318921998224788049.post-66878710190333457302013-10-12T23:58:00.000+02:002013-10-22T22:50:06.072+02:00What is a good code review?<div>
Every programmer does mistake in his code, whatever are his programming skills. You can detect these mistakes at different level in the development workflow : from the development itself to the production. What is sure is that the faster you will find a mistake, the faster you will correct it.<br>
<br>
Reread the code, even if it works, is a good way to find potential bugs as fast as possible. This reading is called the code review. <br>
<br>
Every team has his own way to do a code review. Some ways are bad, others are better. In this thread, I will present you what is for me the perfect code review, and we will see that a good code review has others benefits that just reveal potential bugs.</div>
<div>
<br>
<h2>
<span style="font-size: x-large;">How?</span></h2>
The reader and the programmer must read the code together, side by side using the programmer computer. It encourages both of them to discuss and debate about the code in order to challenge it as most as possible.<br>
<br>
The programmer must not introduce the modifications to the reader because the reader must understand himself the code. Indeed, the code must be clear enough because other teammates will have to work on it. So the reader must take the mouse and keyboard command.<br>
<br>
To be sure to read every modified line of code, the review must be done from the modifications diff of the version control software. Of course, if necessary, the reader can have a look in the entire code.<br>
<br>
If the reader see something which must be changed in the code, the programmer must take a note to don't forget to change it after the code review. Every modification must be done just after the code review.</div>
<div>
<br>
<h2>
<span style="font-size: x-large;">What?</span></h2>
<div>
The reader has the responsibility to approve that the code is correct. If he doesn't see a bug during the code review, he will have the same responsibility than the programmer if a problem occurs in production. So every line of code must be checked and understood.<br>
<br>
Several kind of problems can be detected in the code and fixed with the code review. For examples, the reader could do the following remarks to the programmer :<br>
<br>
<ul>
<li>"The format of these two lines of code doesn't respect our coding conventions"</li>
<li>"When I read the method name, I don't understand what is his responsibility"</li>
<li>"In this case, you will have a NullPointerException with this variable"</li>
<li>"For this line of code, there already exists an utility method in the util package"</li>
<li>"A unit test is missing for this case"</li>
<li>"Here, you are doing a database access in a loop, you should refactor your code"</li>
<li>...</li>
</ul>
<div>
As you see, the code review allows to be sure that the code will respect the coding conventions, that the code will be understandable, and that potential bugs can be detected.</div>
<div>
<br></div>
</div>
<div>
<h2>
<span style="font-size: x-large;">When?</span></h2>
</div>
<div>
When the development of a feature is finished, the developer has to demonstrate his work and to ask somebody to read his code.<br>
<br>
If the developer starts with the code review and finishes with the demonstration, it is possible that he has to modify his code after the demonstration because the feature doesn't fit all the functional requirements. So a second code review will be necessary.<br>
<br>
On the other side, if he starts with the demonstration and finishes with the code review, it is possible that he has to modify his code. But in this case, as the developer directly understood the functional requirements and wrote regression tests, it is not necessary to re-demonstrate the feature.<br>
<br>
So the best way is to start to demonstrate the feature and to finish with the code review.<br>
<br>
<h2>
<span style="font-size: x-large;">Who?</span></h2>
</div>
<div>
Who must read the developer code? The technical leader? A senior developer? A junior developer? Depending on the case, every teammate must be able to do a code review. Two parameters must be taken into account to decide who can read who : The first one is the technical level of both the developer and the reader. The second one is the criticality of the feature.<br>
<br>
For a critical feature (something complex or really important for the business), the reader must be experimented, so either a senior developer or the technical leader.<br>
<br>
For a "basic" feature, it is better to avoid to let a junior developer read another junior developer. Indeed they may don't know every coding conventions or miss bugs or performance issues. But a junior developer can read a senior developer : it is a good way for the junior to improve his competences.<br>
<br></div>
<div>
<div>
<h2>
<span style="font-size: x-large;">Conclusion : benefits of a good code review</span></h2>
</div>
<div>
A good code review allows you to fix several bugs and to refactor your code really quickly. You have so the insurance to have a best quality in your software and in your code.<br>
<br>
But more than that, encourage your team to read the code allows them to have a best knowledge of the code and to improve their competences very easily.<br>
<br>
So if you think that this kind of code review is a waste of time, you are mistaken because you will have less bugs in production, a code of quality, and to finish a more competent team, so a more effective team.<br>
<br></div>
</div>
</div>
Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-91585783930829984922013-09-29T12:38:00.001+02:002013-09-29T12:38:16.002+02:00Gatling dynamic parameters exampleIt is very easy to create a stress test with <a href="http://jsebfranck.blogspot.fr/2013/08/easy-stress-tests-with-gatling.html">Gatling</a> when you know the parameters to send within each HTTP request. But sometimes you have to handle dynamic parameters and this is more complicated to manage.<br />
<br />
A typical case where dynamic parameters are needed is when you are testing a page with a form protected with a <a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery">CSRF</a> protection, for example :<br />
<div>
<br />
<pre style="background-color: #f0f0f0; border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 646.46875px;"><code style="word-wrap: normal;"><form action="/deleteAccount" method="POST">
<input type="hidden" name="login" value="jsebfranck"/>
<input type="hidden" name="verificationToken" value="wQvhCLsPHVbKd9ANoOZTpBsXtVhpG0h3"/>
</form>
</code></pre>
<br />
For each display of this form, the verificationToken parameter expected on the server side will be different. This protection against CSRF attacks allows to be sure that the POST request is send from a form generated by the website, and not from another website.</div>
<br />
If you use the gatling recorder to display the form page then submit the form, it will generate the following test :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">val scn = scenario("delete account")
.exec(http("get delete account page")
.get("/Account/Delete")
.headers(headers_1))
.exec(http("delete account")
.post("/Account/Delete")
.headers(headers_5)
.param("verificationToken", "wQvhCLsPHVbKd9ANoOZTpBsXtVhpG0h3")
.param("login", "jsebfranck"))
</code></pre>
<br />
The problem with this test is that it always sends the same verificationToken parameter within the form. So if you launch it, it will failed because the verificationToken will be different from the expected verificationToken.<br />
<br />
To resolve this problem you have to parse the page with the form in order to retrieve the verificationToken parameter and to send it within the form. To do that you can use two advanced features of gatling, the "session" and the "saving data" features :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">val scn = scenario("delete account")
.exec(http("get delete account page")
.get("/Account/Delete")
.headers(headers_1)
.check(
regex("""<input name="verificationToken" type="hidden" value="([^"]*)" />""")
.saveAs("requestVerificationToken")))
.exec(http("delete account")
.post("/Account/Delete")
.headers(headers_5)
.param("verificationToken", "${requestVerificationToken}")
.param("login", "jsebfranck"))
</code></pre>
<br />
The check method allows to do operations on the result of the request. Here we apply a regex to get the content of the value attribute, then we save it in the requestVerificationToken variable with the saveAs method :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> .check(
regex("""<input name="verificationToken" type="hidden" value="([^"]*)" />""")
.saveAs("requestVerificationToken")))
</code></pre>
<br />
Then we can reuse the requestVerificationToken variable to send it within the form :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> .param("verificationToken", "${requestVerificationToken}")
</code></pre>
<br />
Now you are able to use dynamic parameters in a Gatling test. I hope this example will help you to bypass most of the constraints of a website. For further information on gatling advanced usages, please visit this <a href="https://github.com/excilys/gatling/wiki/Advanced-Usage">page</a>.<br />
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com2tag:blogger.com,1999:blog-4318921998224788049.post-61147515238625630072013-09-26T21:39:00.000+02:002013-09-26T21:41:42.745+02:00Keep your code simple with YAGNIOne key principle of success for a web site is its capacity to always improve itself with new features and to do it faster than its competitors. To achieve that, the IT team must be able to create features as fast as possible at the different phases of the realization : development, tests and deployment.<br />
<br />
During the development phase, one axis to work faster is to always have the more simple, readable and modifiable code. Besides several practices well known like code review or refactoring, there is an important principle which is not very well known by developers : YAGNI. Before explaining this principle, let's see how could a developer work without YAGNI.<br />
<br />
<h2>
<span style="font-size: x-large;">Scenario without YAGNI</span></h2>
<div>
A agile development team has in its backlog several features and only the three first features are absolutely mandatory before the deployment in production :</div>
<div>
<ul>
<li>Creation of a user (must have)</li>
<li>User listing (must have)</li>
<li>Deletion of a user (must have)</li>
<li>Modification of a user (nice to have)</li>
<li>Search a user by login (nice to have)</li>
</ul>
<div>
A developer begins to work on the "Creation of a user example" feature. To do that he has to create the user interface, the business logic and the DAO layer.</div>
</div>
<div>
<br /></div>
<div>
When he creates the DAO layer, he adds a method "createUser" and then he directly creates several others methods : "getUsers", "deleteUser", "updateUser" and "searchUserByLogin". The developer did that because he is quite sure that these methods will be necessary for the four other features of the backlog.</div>
<div>
<br /></div>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">public interface UserDao {
public void createUser(User user);
public void updateUser(User user);
public void deleteUser(User user);
public User searchUserByLogin(String login);
public List<user> getUsers();
}
</user></code></pre>
<div>
<br /></div>
<h3>
Then specifications change</h3>
<div>
The three mandatory features are developed and pushed in production. </div>
<div>
<br /></div>
<div>
Having directly the feedback of the use of these three features in production, the two remaining features specifications change :</div>
<div>
<ul>
<li><strike>Modification of a user</strike> : it is not necessary anymore to modify a user as it is easy to delete a user and to create a new one</li>
<li>Search a user <strike>by login</strike> by first name : is is finally better to search the user by his first name.</li>
</ul>
</div>
<h3>
And the developer modifies the code</h3>
<div>
As it is not necessary anymore to modify a user, the method "updateUser" has been created for nothing. And as the user search must be done by the first name rather than by the user login : the searchUserByLogin method must be changed.</div>
<div>
<br /></div>
<div>
The developer still thinks that the "updateUser" method could be useful in the future, so he doesn't delete it. Moreover, he keeps the "searchUserByLogin" method and create a new method "searchUserByFirstName".</div>
<div>
<br /></div>
<div>
So finally in the DAO layer, we have six methods but only four methods are used :<br />
<br /></div>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">public interface UserDao {
public void createUser(User user);
public void updateUser(User user); // useless
public void deleteUser(User user);
public User searchUserByLogin(String login); // useless
public User searchUserByFirstName(String firstName);
public List<user> getUsers();
}</user></code></pre>
<div>
<br /></div>
<div>
The main mistake the developer did is to add unnecessary code in his program because he was thinking it could be useful in the future. The second mistake he did is to don't clean his code after the specifications change.</div>
<div>
<br /></div>
<div>
Maybe you don't realize why these two methods can be a problem in your code. So try to imagine several developers doing this mistake every day : it will be quickly the mess in your code.</div>
<div>
<br /></div>
<div>
<h2>
<span style="font-size: x-large;">You Ain't Gonna Need It!</span></h2>
Based on the fact that we cannot know or guess the future needs of a software, the idea of YAGNI is to never add code or feature which is not immediately necessary.<br />
<br />
More than avoid to have useless methods in the code, the main advantages of the YAGNI principle are : <br />
<ul>
<li>Your code is smaller, so more simple </li>
<li>You don't have to maintain unnecessary code so you win time when you refactore your code </li>
<li>You don't have pieces of code which have never been tested in production and you don't risk to use it thinking it is working</li>
</ul>
<ul style="font-size: medium; font-weight: normal;">
</ul>
<div>
<span style="font-size: small;"><span style="font-weight: normal;"><br /></span></span></div>
</div>
Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-25874992248297953582013-08-29T23:42:00.000+02:002013-08-30T08:59:45.719+02:00Easy stress tests with GatlingA few month ago, I needed to compare the response time of several webapp pages before and after upgrading a cache component. I thought the easier and faster way to do that would be to write a shell script doing several http calls to the webapp using wget, and to compute myself the average response time. I did that, it worked and I had my expected conclusion that the new cache version was faster. Of course, I wrote my shell script in a dirty way and it finally finished to the trash.<br />
<br />
Then I hear about <a href="http://gatling-tool.org/">Gatling</a>, a simple alternative to JMeter to do stress tests. I tested it and indeed it was very easy to use it, and even faster than creating an homemade script.<br />
<br />
Today I would like to show you how it is easy to write a stress test with this tool.<br />
<span style="font-size: x-large;"><br /></span>
<br />
<h2>
<span style="font-size: x-large;">Start easily with the recorder</span></h2>
<br />
<div>
<br />
Gatling provides an HTTP recorder tool allowing to generate a Gatling test. It is a good way to generate a first test.<br />
<br />
To do that :<br />
<br />
1. Download a Gatling archive and extract it<br />
2. Launch the recorder : ./bin/recorder.bat or ./bin/recorder.sh<br />
3. Configure where the test must be generated, for that you can change the fields "package", "class name", and "ouput folder" :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://2.bp.blogspot.com/-Z7zWdlcjlHs/UheDtX-2r7I/AAAAAAAAAE8/WhelTjGuxTY/s1600/gatling-recorder.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://2.bp.blogspot.com/-Z7zWdlcjlHs/UheDtX-2r7I/AAAAAAAAAE8/WhelTjGuxTY/s1600/gatling-recorder.JPG" height="478" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Gatling recorder configuration</td></tr>
</tbody></table>
<br />
4. Configure your web browser to use Gatling as a proxy. As see in the recorder configuration, the http port is 8000 and the https port is 8001.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-DXZSD0M_R3U/UheBedzgSSI/AAAAAAAAAEo/KS4DIiuFAsA/s1600/gatling-proxy.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-DXZSD0M_R3U/UheBedzgSSI/AAAAAAAAAEo/KS4DIiuFAsA/s1600/gatling-proxy.JPG" height="640" width="584" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Web browser configuration</td></tr>
</tbody></table>
5. Press the start button in the recorder<br />
6. Go to the pages you want to test in the web browser<br />
7. Press the stop & save button in the recorder<br />
8. Open the generated test. In this example the test has been generated under C:\gatling-stress-tests\com\mycompany\stresstests\MyFirstStressTest.scala<br />
9. Delete the useless requests. For example in my stress test I don't want the requests retrieving the static resources or the google analytics requests.<br />
10. Rename the scenario and the http methods giving comprehensive titles : we will find these titles in the test reports.<br />
11. Change the count of users to do 50 simultaneous connections to the webapp<br />
<br />
Congratulations you have created your first Gatling test :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">import io.gatling.core.Predef._
import io.gatling.core.session.Expression
import io.gatling.http.Predef._
import io.gatling.jdbc.Predef._
import io.gatling.http.Headers.Names._
import io.gatling.http.Headers.Values._
import scala.concurrent.duration._
import bootstrap._
import assertions._
class MyFirstStressTest extends Simulation {
val httpProtocol = http.baseURL("http://mywebsite.com")
.acceptHeader("image/png,image/*;q=0.8,*/*;q=0.5")
.acceptEncodingHeader("gzip, deflate")
.acceptLanguageHeader("fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3")
.connection("keep-alive")
.userAgentHeader("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0")
val headers_1 = Map("""Accept""" -> """text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8""")
val headers_5 = Map("""Accept""" -> """text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8""",
"""Content-Type""" -> """application/x-www-form-urlencoded""")
val scn = scenario("my first test")
.exec(http("home page").get("""/""").headers(headers_1))
.exec(http("login page").get("""/Account/Login""").headers(headers_1))
.exec(http("login").post("""/Account/Login""").headers(headers_5)
.param("""UserName""", """jsebfranck""")
.param("""Password""", """my password""")
.exec(http("news").get("""/News""").headers(headers_1))
setUp(scn.inject(atOnce(50 user))).protocols(httpProtocol)
}
</code></pre>
<br />
As you can see, a Gatling test is a Scala script. Don't worry if you don't know Scala, this script is easy to understand, you don't have to understand all Scala mechanisms :<br />
<ul>
<li>httpProtocol variable defines the global parameters to connect to the website</li>
<li>headers_1 and headers_5 defines http headers parameters </li>
<li>scn is the test scenario with the title "my first test"</li>
<li>the test contains 4 http calls (3 GET and 1 POST) called "home page", "login page", "login" and "news"</li>
<li>setUp allows to launch the test</li>
<li>atOnce(50 user) indicates that 50 connections will be done simultaneously</li>
</ul>
<br />
<h2>
<span style="font-size: x-large;"><b>Launch the test</b></span></h2>
Launch a Gatling test is very easy :<br />
<br />
1. Launch the gatling executable : ./bin/gatling.bat or ./bin/gatling.sh<br />
2. Select the MyFirstStressTest test<br />
3. Open the HTML report generated in results folder<br />
<br />
Several graphics are generated in the report. But two graphics are particularly interesting. The first one give for each HTTP request :<br />
<ul>
<li>the minimum, maximum and average response time</li>
<li>the standard deviation : deviation of the requests response time compared to the average response time. A small standard deviation means that the response time is almost the same for each request</li>
<li>the 95 percentile : means that 95% of the requests are under this response time </li>
<li>the 99 percentile : means that 99% of the requests are under this response time</li>
</ul>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-yu48rpSZZiM/Uh0EzDj6y1I/AAAAAAAAAFg/2t-IhjQGTtk/s1600/Capture+d%E2%80%99e%CC%81cran+2013-08-27+a%CC%80+21.58.59.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-yu48rpSZZiM/Uh0EzDj6y1I/AAAAAAAAAFg/2t-IhjQGTtk/s1600/Capture+d%E2%80%99e%CC%81cran+2013-08-27+a%CC%80+21.58.59.png" height="210" width="640" /></a></div>
<br />
If you click on a request title, for example "news", you get more information specific to the request. Here we can see the response time evolution based on the active sessions.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-2JOH4wXAzvA/Uh0GyI9CNHI/AAAAAAAAAFs/Fq-rE38s50Y/s1600/Capture+d%E2%80%99e%CC%81cran+2013-08-27+a%CC%80+22.08.52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-2JOH4wXAzvA/Uh0GyI9CNHI/AAAAAAAAAFs/Fq-rE38s50Y/s1600/Capture+d%E2%80%99e%CC%81cran+2013-08-27+a%CC%80+22.08.52.png" height="256" width="640" /></a></div>
<br /></div>
<div>
<h2>
<span style="font-size: x-large;">Conclusion</span></h2>
I hope you understood it is not complicated to use a stress test tool like Gatling, even if it is rare for you to do performance benchmarks. This example is very basic and doesn't show all great features of Gatling. If you want to go further, I encourage you to read the following documentations :<br />
<ul>
<li><a href="https://github.com/excilys/gatling/wiki/First-Steps-with-Gatling">First steps with gatling</a></li>
<li><a href="https://github.com/excilys/gatling/wiki/Advanced-Usage">Gatling advanced usage</a></li>
</ul>
<div>
<br /></div>
</div>
Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com3tag:blogger.com,1999:blog-4318921998224788049.post-40575210521190764542013-08-25T00:10:00.001+02:002013-12-29T11:55:17.872+01:00EJB 2 on Jboss 7.1 exampleI recently have add the opportunity to migrate an historical JEE web application from JBoss 4 to JBoss 7.1. One of the more complicated point I faced was the migration of the EJB2 components. The old xml configuration was not correct anymore and it was very difficult to find documentation, tutorials or concrete examples, even on JBoss forums. Of course this is understandable because this old version of EJB is not used anymore (hopefully!), but it still exists in several historical web applications.<br />
<div>
<br />
The goal of this new thread is not to explain all the EJB 2 mechanisms but to share what I really missed during the migration, that is a working example of EJB2 sessions and EJB2 entities on JBoss 7.1. Since JEE 6, it is possible to package the EJB directly in a war archive, so this example will be based on that.<br />
<br />
The source code is available <a href="https://github.com/jsebfranck/jee-samples">here</a>.</div>
<br />
<h2>
<span style="font-size: x-large;"><b>EJB 2 session java code</b></span></h2>
I will create an EJB 2 session called HelloWorld with a simple method returning a String object. Three classes must be created to do that :<br />
<br />
<ul>
<li>The EJB interface</li>
</ul>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface HelloWorldEJB extends EJBObject {
public String helloWorld() throws RemoteException;
}
</code></pre>
<br />
<ul>
<li>The implementation</li>
</ul>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class HelloWorldEJBBean implements SessionBean {
private static final long serialVersionUID = 1L;
public String helloWorld() throws RemoteException {
return "Hello world, I am an EJB2 session";
}
public void ejbActivate() throws EJBException, RemoteException {}
public void ejbPassivate() throws EJBException, RemoteException {}
public void ejbRemove() throws EJBException, RemoteException {}
public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException {}
}
</code></pre>
<br />
<ul>
<li>And the EJB Home</li>
</ul>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface HelloWorldEJBHome extends EJBHome {
public HelloWorldEJB create() throws RemoteException, CreateException;
}
</code></pre>
<br />
<h2>
<span style="font-size: x-large;"><b>EJB 2 entity java code</b></span></h2>
<div>
For the EJB 2 entity example, I will create an object called Member with two fields : an id and a login. Three java objects are also needed to do that :</div>
<div>
<br /></div>
<div>
<ul>
<li>The EJBLocalObject</li>
</ul>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">package com.jsebfranck.jboss.ejb2.entity;
import javax.ejb.EJBLocalObject;
public interface Member extends EJBLocalObject {
public Long getId();
public String getLogin();
}
</code></pre>
</div>
<ul>
<li>The EntityBean</li>
</ul>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">package com.jsebfranck.jboss.ejb2.entity;
import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
public abstract class MemberBean implements EntityBean {
private static final long serialVersionUID = 1L;
private transient EntityContext ctx;
public MemberBean() {
}
public Long ejbCreate(Long id, String login) throws CreateException {
setId(id);
setLogin(login);
return id;
}
public void ejbPostCreate(Long id, String login) {
}
public abstract Long getId();
public abstract void setId(Long id);
public abstract String getLogin();
public abstract void setLogin(String login);
public void setEntityContext(EntityContext ctx) {
this.ctx = ctx;
}
public void unsetEntityContext() {
this.ctx = null;
}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbLoad() {}
public void ejbStore() {}
public void ejbRemove() {}
}
</code></pre>
<br />
<ul>
<li>And the EJBHome</li>
</ul>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">package com.jsebfranck.jboss.ejb2.session;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface HelloWorldEJBHome extends EJBHome {
public HelloWorldEJB create() throws RemoteException, CreateException;
}
</code></pre>
<br />
<div>
<h2>
<span style="font-size: x-large;"><b>XML configuration</b></span></h2>
</div>
<br />
Now we have to declare the EJB entity and the EJB session in the xml configuration. A first file is needed for that, ejb-jar.xml. This file must be put in the /WEB-INF folder of the war archive.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><ejb-jar version="3.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
<enterprise-beans>
<session>
<ejb-name>HelloWorldEJB</ejb-name>
<home>com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome</home>
<remote>com.jsebfranck.jboss.ejb2.session.HelloWorldEJB</remote>
<ejb-class>com.jsebfranck.jboss.ejb2.session.HelloWorldEJBBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
<entity>
<ejb-name>MemberEJB</ejb-name>
<local-home>com.jsebfranck.jboss.ejb2.entity.MemberHome</local-home>
<local>com.jsebfranck.jboss.ejb2.entity.Member</local>
<ejb-class>com.jsebfranck.jboss.ejb2.entity.MemberBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Long</prim-key-class>
<reentrant>false</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>member</abstract-schema-name>
<cmp-field>
<field-name>id</field-name>
</cmp-field>
<cmp-field>
<field-name>login</field-name>
</cmp-field>
<primkey-field>id</primkey-field>
</entity>
</enterprise-beans>
</ejb-jar>
</code></pre>
<br />
Now let's configure the mapping of the EJB entity. This is done in the jbosscmp-jdbc.xml file which must be put in the META-INF folder of the war archive.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><jbosscmp-jdbc>
<defaults>
<datasource>java:jboss/datasources/hsqldbDS</datasource>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>MemberEJB</ejb-name>
<row-locking>false</row-locking>
<table-name>simple</table-name>
<cmp-field>
<field-name>id</field-name>
<column-name>id</column-name>
</cmp-field>
<cmp-field>
<field-name>login</field-name>
<column-name>login</column-name>
</cmp-field>
</entity>
</enterprise-beans>
</jbosscmp-jdbc></code></pre>
<span style="font-size: x-large;"><br /></span>
<br />
<h2>
<span style="font-size: x-large;">JBoss configuration</span></h2>
As you noticed in the jbosscmp-jdbc.xml file, a datasource is required for the entity. This datasource is configured directly in the jboss configuration. As I launch JBoss 7.1 in a standalone mode, I put the following configuration in the $JBOSS_HOME/standalone/configuration/standalone-full.xml file.<br />
<br />
In this example, a use a hsql database :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><div class="p1">
</div>
<subsystem xmlns="urn:jboss:domain:datasources:1.0">
<datasources>
<datasource enabled="true" jndi-name="java:jboss/datasources/hsqldbDS" jta="true" pool-name="hsqldbDS" use-ccm="true" use-java-context="true">
<connection-url>jdbc:hsqldb:file:/Users/jsebfranck/Documents/database/standaloneHsqldb</connection-url>
<driver>hsqldb</driver>
<pool>
<prefill>false</prefill>
<use-strict-min>false</use-strict-min>
<flush-strategy>FailingConnectionOnly</flush-strategy>
</pool>
<security>
<user-name>sa</user-name>
</security>
</datasource>
</code></pre>
<span style="font-size: x-large;"><br /></span>
<br />
<h2>
<span style="font-size: x-large;">Deployment in jboss</span></h2>
When you deploy the web application on jboss (<i>standalone.sh --server-config=standalone-full.xml</i>), you can see the JNDI names of both EJB in the logs. This will help us to do our lookups in the client.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">
19:07:56,689 INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) JBAS015876: Starting deployment of "WarWithEJB2.war"
19:07:57,069 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-4) JNDI bindings for session bean named MemberEJB in deployment unit deployment "WarWithEJB2.war" are as follows:
java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
java:app/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
java:module/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome
java:app/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome
java:module/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome
19:07:57,073 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-4) JNDI bindings for session bean named HelloWorldEJB in deployment unit deployment "WarWithEJB2.war" are as follows:
java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
java:app/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
java:module/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
java:jboss/exported/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
java:app/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
java:module/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
java:jboss/exported/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
</code></pre>
<br />
<h2>
<span style="font-size: x-large;">Simple servlet client</span></h2>
We are now able to test our EJBs. The following code calls the helloWorld method of the EJB session, then it creates a new line in the Member table.<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">package com.jsebfranck.jboss.servlet;
import java.io.IOException;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jsebfranck.jboss.ejb2.entity.Member;
import com.jsebfranck.jboss.ejb2.entity.MemberHome;
import com.jsebfranck.jboss.ejb2.session.HelloWorldEJB;
import com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome;
@WebServlet("/Ejb2Servlet")
public class Ejb2Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
// EJB 2 session
HelloWorldEJBHome helloWorldEJBHome = (HelloWorldEJBHome) new InitialContext().lookup("java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome");
HelloWorldEJB helloWorldEjb = helloWorldEJBHome.create();
response.getWriter().println("EJB session test : " + helloWorldEjb.helloWorld());
// EJB 2 entity
MemberHome memberHome = (MemberHome) new InitialContext().lookup("java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome");
Member member = memberHome.create(25L, "jsebfranck");
response.getWriter().println("EJB entity test : " + member.getId() + " - " + member.getLogin());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
</code></pre>
<br />
And we have the prove that this example is working :-)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-WzAT5_0AwfM/Uhj1vm1cVbI/AAAAAAAAAFM/jHRxaB_E8Bc/s1600/Capture+d%E2%80%99e%CC%81cran+2013-08-24+a%CC%80+20.06.25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-WzAT5_0AwfM/Uhj1vm1cVbI/AAAAAAAAAFM/jHRxaB_E8Bc/s1600/Capture+d%E2%80%99e%CC%81cran+2013-08-24+a%CC%80+20.06.25.png" height="166" width="400" /></a></div>
<br />
<h2>
<b><span style="font-size: x-large;">Conclusion</span></b></h2>
I hope this example helped you to migrate your EJB2 to a newer version of your application server. Don't hesitate to contact me if you need further details to advance in your migration.<br />
<br />
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com14tag:blogger.com,1999:blog-4318921998224788049.post-86640679704405766632013-07-21T23:09:00.003+02:002013-07-21T23:09:35.982+02:00ElasticSearch quick startIn the beginning of July, I assisted in an ElasticSearch workshop organized by <a href="http://www.xebia.fr/">Xebia</a>. It was a great event which gave me the opportunity to understand this tool basis. If you never heard a word about ElasticSearch, go to <a href="http://www.elasticsearch.org/">http://www.elasticsearch.org</a>. To resume quickly, it is a NoSQL search engine based on Apache Lucene.<br />
<br />
Today I would like to show you how it is easy to begin with this tool.<br />
<br />
<h2>
<span style="font-size: x-large;">First launch </span></h2>
<ol>
<li>Download an ElasticSearch archive on <a href="http://www.elasticsearch.org/download/">http://www.elasticsearch.org/download/</a></li>
<li>Extract the archive</li>
<li>Launch elasticsearch : </li>
</ol>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ ./bin/elasticsearch -f
[2013-07-12 17:09:08,776][INFO ][node ] [Centurius] {0.90.2}[2741]: initializing ...
[2013-07-12 17:09:08,785][INFO ][plugins ] [Centurius] loaded [], sites []
[2013-07-12 17:09:11,253][INFO ][node ] [Centurius] {0.90.2}[2741]: initialized
[2013-07-12 17:09:11,253][INFO ][node ] [Centurius] {0.90.2}[2741]: starting ...
[2013-07-12 17:09:11,372][INFO ][transport] [Centurius] bound_address {inet[/0.0.0.0:9300]}, publish_address {inet[myComputer/127.0.0.1:9300]}
[2013-07-12 17:09:14,470][INFO ][cluster.service] [Centurius] new_master [Centurius][cuT7Cb_eQbaHyQ9fW9aCng][inet[myComputer.local/127.0.0.1:9300]], reason: zen-disco-join (elected_as_master)
[2013-07-12 17:09:14,514][INFO ][discovery] [Centurius] elasticsearch/cuT7Cb_eQbaHyQ9fW9aCng
[2013-07-12 17:09:14,542][INFO ][http ] [Centurius] bound_address {inet[/0.0.0.0:9200]}, publish_address {inet[myComputer.local/127.0.0.1:9200]}
[2013-07-12 17:09:14,543][INFO ][node ] [Centurius] {0.90.2}[2741]: started
[2013-07-12 17:09:14,595][INFO ][gateway ] [Centurius] recovered [0] indices into cluster_state
</code></pre>
<br />
ElasticSearch is now running! You can check that by going to <a href="http://localhost:9200/">http://localhost:9200</a> in your web browser. Centurius is the name of the launched instance. You can override it by changing the property node.name in the elasticsearch.yml file.<br />
<br />
You should only try to change the configuration if you encounter a problem. The default configuration is good enough to bring the server in production.<br />
<br />
<h2>
<span style="font-size: x-large;">Basic queries</span></h2>
ElasticSearch provides a restful API allowing to create, search or delete documents. So to create documents you have to do a HTTP POST or PUT request, to search documents you have to do a GET request, and to delete documents you have to do a DELETE request.<br />
<br />
<h4>
Create a document</h4>
<br />
I want to create a simple database containing e-commerce products. Each document is created in a specific index under a specific type. From whose coming from SQL world like me, the index is similar to a SQL database instance whereas the type is similar to a SQL database table.
To store my products documents, I call the index “catalog” and the type “product”. The document content must be in JSON.<br />
<br />
To create a document, you have to respect the following syntax :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ curl -XPOST 'http://<host name>/<index>/<type>/' -d '{<your json document>}'</code></pre>
<br />
For example :
<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ curl -XPOST 'http://localhost:9200/catalog/product/' -d '{
"title" : "Ipad 2", "manufacturer" : "Apple", "submissionDate" : "2013-07-12",
"text" : "tablette tactile" }'
{"ok":true,"_index":"catalog","_type":"product",
"_id":"iZ_SSvyBRbOBYeRHWIjSaA","_version":1} </code></pre>
<br />
Congratulations, you have created your first document. Each document has an unique identifier. Here the identifier has been generated automaticaly because I did a POST request, it is "iZ_SSvyBRbOBYeRHWIjSaA".
<br />
<br />
<h4>
List all documents</h4>
<br />
To check that the document has been correctly created, you can list all the indexed documents with the following command :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ curl -XGET 'http://localhost:9200/catalog/_search'
{"took":2,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},
"hits":{"total":1,"max_score":1.0,"hits":[{"_index":"catalog","_type":"product",
"_id":"iZ_SSvyBRbOBYeRHWIjSaA","_score":1.0, "_source" : { "title" : "Ipad 2",
"manufacturer" : "Apple", "submissionDate" : "2013-07-12",
"text" : "tablette tactile" }}]}}
</code></pre>
<br />
<h4>
Search documents by term</h4>
<br />
If you want to search every document with a title equals to "ipad" :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ curl -XGET 'http://localhost:9200/catalog/product/_search' -d '{
"query" : {
"match" : {
"title" : "ipad"
}
}
}'
</code></pre>
<br />
Please note that you can search documents in an entire index (/catalog) or just on a type (/catalog/product).<br />
<br />
<h4>
Search documents with wildcard query</h4>
<br />
If you want to search every document with a title starting with "ip" :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ curl -XGET 'http://localhost:9200/catalog/product/_search' -d '{
"query" : {
"wildcard" : {
"title" : "ip*"
}
}
}'
</code></pre>
<br />
<h4>
Search documents with fuzzy query</h4>
<br />
If you want to search a document from a term potentially misspelled, you can do a fuzzy query. For example the following query allows to search the products with a title like ipod or ipad :
<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ curl -XGET 'http://localhost:9200/catalog/product/_search' -d '{
"query" : {
"fuzzy" : {
"title" : "ipud"
}
}
}'
</code></pre>
<br />
<h4>
Delete a document</h4>
<br />
If you want to delete a document, you have to do a DELETE HTTP request :
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ curl -XDELETE 'http://localhost:9200/catalog/product/_query' -d '{
"match" : {
"title" : "Iphone 5"
}
}'
</code></pre>
<br />
<h2>
<span style="font-size: x-large;">
Understand ElasticSearch clustering</span></h2>
The ElasticSearch-Head plugin allows to see the cluster state. To install it :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">$ ./bin/plugin -install mobz/elasticsearch-head
-> Installing mobz/elasticsearch-head...
Trying https://github.com/mobz/elasticsearch-head/zipball/master... (assuming
site plugin)
Downloading ...........DONE
Identified as a _site plugin, moving to _site structure ...
Installed head
</code></pre>
<br />
Now, without restarting ElasticSearch, you can check the cluster state here : <a href="http://localhost:9200/_plugin/head">http://localhost:9200/_plugin/head</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-vDDqep3yc3Y/UewBNyxIV5I/AAAAAAAAACw/CDnNnb6DN3c/s1600/Capture+d%E2%80%99e%CC%81cran+2013-07-12+a%CC%80+17.34.42.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-vDDqep3yc3Y/UewBNyxIV5I/AAAAAAAAACw/CDnNnb6DN3c/s1600/Capture+d%E2%80%99e%CC%81cran+2013-07-12+a%CC%80+17.34.42.png" height="188" width="640" /></a></div>
<br />
<br />
<br />
<br />
We can find our ElasticSearch instance called Centurius. This instance is composed, by default, of five shards (property index.number_of_shards). Each shard is a Lucene instance automatically managed by ElasticSearch. When you create a document, the document id is used to determine in which shard it must be stored.<br />
<br />
Now we can launch a new instance of ElasticSearch (elasticsearch -f in a new tab of your console). If you refresh the ElasticSearch-Head page, you constat that a new instance apperead in the cluster :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-lUukPSCpPwQ/UewBe3kDt1I/AAAAAAAAAC4/NIUSvBmtqvY/s1600/Capture+d%E2%80%99e%CC%81cran+2013-07-12+a%CC%80+17.37.11.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-lUukPSCpPwQ/UewBe3kDt1I/AAAAAAAAAC4/NIUSvBmtqvY/s1600/Capture+d%E2%80%99e%CC%81cran+2013-07-12+a%CC%80+17.37.11.png" height="192" width="640" /></a></div>
<br />
If you check the logs of the first instance :
<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">[2013-07-12 17:35:42,209][INFO ][cluster.service] [Centurius] added
{[Reaper][Zqma1_5qQLCxbA0JQAO1BA][inet[/192.168.0.37:9301]],},
reason: zen-disco-receive(join from node[[Reaper][Zqma1_5qQLCxbA0JQAO1BA]
[inet[/192.168.0.37:9301]]])
</code></pre>
<br />
<br />
The new ElasticSearch instance has been automatically discovered by unicast (by default). Each ElasticSearch instance from the same network and with the same cluster name is gathered in the same cluster. You can change the cluster name of an instance in the file config/elasticsearch.yml, property cluster.name.<br />
<br />
Now let's add a third instance of ElasticSearch :<br />
<div>
<code style="word-wrap: normal;"><br /></code></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-cIRs792gm8U/UewBk_6vEcI/AAAAAAAAADA/qRdwFRHujbo/s1600/Capture+d%E2%80%99e%CC%81cran+2013-07-12+a%CC%80+17.38.58.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-cIRs792gm8U/UewBk_6vEcI/AAAAAAAAADA/qRdwFRHujbo/s1600/Capture+d%E2%80%99e%CC%81cran+2013-07-12+a%CC%80+17.38.58.png" height="228" width="640" /></a></div>
<br />
<br />
<br />
<br />
By default, each shard is replicated twice (property index.number_of_replicas) : one primary shard used for read and write operations, and one replica shard used for read operations.<br />
<br />
Here the node Centurius contains the primary shards 1, 3 and 4. The node Noh-Varr contains the primary shards 0 and 2. Finally, the node Reaper contains only replica shards.<br />
<br />
<h2>
<span style="font-size: x-large;">Conclusion</span></h2>
<br />
I hope this thread gave you the basis to work with ElasticSearch and to understand its clustering mechanism. Don't hesitate to play with ElasticSearch, change its properties and do more complicated queries! Besides this thread, a good way to start with this tool and to understand every concept is the <a href="http://www.elasticsearch.org/guide/reference/glossary/">ElasticSearch glossary page</a>. Have fun!<br />
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com1tag:blogger.com,1999:blog-4318921998224788049.post-56300818563248895892013-07-01T20:45:00.000+02:002013-07-01T20:45:33.399+02:00Why it is faster to write unit tests?"We don't have the time to write unit tests", "we write the unit tests because it is mentioned in the definition of done", "we write the unit tests at the end of the sprint"... How many times did you hear or maybe say that?<br />
<br />
The purpose of this
thread is to prove that writing unit tests, particularly before testing in runtime, can help you to be more productive and efficient. To begin, let's compare the development process with and without unit tests.<br />
<div>
<br />
<h2>
<span style="font-size: x-large;">Without unit tests</span></h2>
<div>
How a developer works without unit tests?</div>
<ol>
<li><u>Development phase </u></li>
<ul>
<li>Write the entire code of the feature</li>
</ul>
<li><u>Runtime test phase</u></li>
<ol>
<li>Constat a bug</li>
<li>Analyse where the bug comes from </li>
<li>Fix the bug</li>
<li>Test if the correction is correct </li>
<li>Continue the test phase until all tests are correct</li>
</ol>
</ol>
</div>
<div>
<div>
<h2>
<span style="font-size: x-large;">With unit tests</span></h2>
<div>
Now, how a developer works with unit tests?</div>
<ol>
<li><u>Development phase </u></li>
<ol>
<li><span style="color: red;">Write a unit test</span></li>
<li><span style="color: red;">Write the corresponding code</span></li>
<li><span style="color: red;">Write another unit test until the feature is completely developed </span></li>
</ol>
<li><u>Runtime test phase</u></li>
<ol>
<li>Constat a bug</li>
<li>Analyse where the bug comes from </li>
<li><span style="color: red;">Write a unit test reproducing the bug</span></li>
<li>Fix the bug</li>
<li>Test if the correction is correct </li>
<li>Continue the test phase until all tests are correct</li>
</ol>
</ol>
</div>
<div>
<div>
<h2>
<span style="font-size: x-large;">Runtime test phase is expensive</span></h2>
The runtime test phase (with or without unit tests) takes a lot of time for several reasons :</div>
<div>
<ul>
<li>For each test, you have to prepare a set of data, even for the margin cases</li>
<li>You have to launch your application</li>
<li>You have to play your test and maybe find a bug</li>
<li>For each bug, you have to find where to fix the bug, possibly using a debugger</li>
<li>Then, you have to fix your code</li>
<li>Finally, you test again to be sure that your fix is correct. For that, of course, you have to prepare a new set of data</li>
</ul>
<br />
<h2>
<span style="font-size: x-large;">Runtime test phase is longer without unit tests</span></h2>
With unit tests, you can be sure to find most of the bugs directly during the development phase. Of course there are still some bugs but less than if you write no unit tests at all.<br />
<br />
So if you have more bugs without unit tests, the runtime test will be longer.<br />
<br />
Now, how can be sure without unit tests that a bug fix hasn't broke a functional case you have already test? Are you sure all your code is still working? Maybe you should restart the runtime test phase from the beginning? And what about refactoring your code?<br />
<br />
With unit tests, you can be very confident to haven't broke anything because all the tests you have already written can prove you that. Plus, you won't be afraid to do refactoring in order to have a cleaner and more comprehensible code, what will help you to work more efficiently during the next developments.<br />
<br />
<h2>
<span style="font-size: x-large;">Conclusion</span></h2>
Of course it is faster to write a code without doing units
tests. But how much extra time do you spend testing in runtime or fixing bugs in production? <br />
<br />
Less bugs also mean less stress and more time to work on interesting things. <br />
<br />
In bonus, you do more code and less functional tests. That's cool, coding is what you are good for!<br />
<div>
<br /></div>
</div>
</div>
</div>
<div>
<h2>
<span style="font-size: x-large;">What about integration tests?</span></h2>
An answer to resolve the cost of the manual tests are the integration tests. Do you still need to do unit tests with integration tests? It is enough to do only unit tests? It is a very interesting question I will try to answer in another thread. But yes, unit tests and integration tests are complementary.</div>
Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-28367461861231018342013-06-29T22:05:00.000+02:002013-06-29T22:05:13.304+02:00How to test dates in java unit tests?<div>
Testing dates in unit tests is not always an easy stuff. Take a look to the following method, I will then explain you what is the main problem you can encounter.<br />
<div>
<br /></div>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">public class UserService {
@Inject
private UserEventDao userEventDao;
@Inject
private DateUtility dateUtility;
public void createCreationUserEvent(User user) {
UserEvent event = new UserEvent();
event.setUser(user);
event.setUserEventType(UserEventType.CREATION);
event.setEventDate(new Date());
userEventDao.create(event);
}
}
</code></pre>
</div>
<br />
To test this method, you have to check that the UserEvent object passed to the UserEventDao.create method is correctly filled. For that you can use a mock framework like Mockito and write the following test :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
@InjectMocks
private UserService userService;
@Mock
private UserEventDao userEventDao;
@Test
public void createCreationUserEvent_withCorrectParameters_shouldCreateAnEvent() {
// Given
User user = new User();
// When
userService.createCreationUserEvent(user);
// Then
UserEvent expectedUserEvent = new UserEvent();
expectedUserEvent.setUser(user);
expectedUserEvent.setUserEventType(UserEventType.CREATION);
expectedUserEvent.setEventDate(new Date());
verify(userEventDao).create(expectedUserEvent);
verifyNoMoreInteractions(userEventDao);
}
}
</code></pre>
<br />
The problem is that this test is not in success all the time because the Date object precision is in milliseconds and you can have a little difference between the date created in the method and the date created in the test.<br />
<br />
The solution to resolve that is to be sure to manipulate the same Date object between the method and your test. Of course you could add a Date argument in your createCreationUserEvent method but this would just move our problem to the calling method.<br />
<br />
I recommend you two solutions to do that : the first one is to use PowerMock and the second one is to create a DateUtility class.<br />
<br />
<h2>
<span style="font-size: x-large;">Solution 1 : Using PowerMock</span></h2>
<div>
<a href="http://code.google.com/p/powermock">PowerMock</a> is a mock framework allowing you to mock what cannot be mocked by others frameworks : static method calls, private methods, object constructions etc. To do that, Powermock manipulates the bytecode of the class to test.</div>
<div>
<br /></div>
<div>
So in our example, it can mock the new Date() call :</div>
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">@RunWith(PowerMockRunner.class)
@PrepareForTest(UserService.class)
public class UserServiceTest {
@InjectMocks
private UserService userService;
@Mock
private UserEventDao userEventDao;
@Test
public void createCreationUserEvent_withCorrectParameters_shouldCreateAnEvent()
throws Exception {
// Given
User user = new User();
Date eventDate = new Date();
whenNew(Date.class).withNoArguments().thenReturn(eventDate);
// When
userService.createCreationUserEvent(user);
// Then
UserEvent expectedUserEvent = new UserEvent();
expectedUserEvent.setUser(user);
expectedUserEvent.setUserEventType(UserEventType.CREATION);
expectedUserEvent.setEventDate(eventDate);
verify(userEventDao).create(expectedUserEvent);
verifyNoMoreInteractions(userEventDao);
}
}
</code></pre>
<br />
As you surely noticed, we use the PowerMock runner and a PrepareForTest annotation which indicates to Powermock that the UserService class bytecode must be modified.<br />
<br />
You can now be sure that your test will be in success at each execution.<br />
<br />
<h2>
<span style="font-size: x-large;">Solution 2 : Creating a date utility class</span></h2>
In this solution, the concept is to delegate the creation of the date to a new class :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">public class DateUtility {
public Date getCurrentDate() {
return new Date();
}
}
</code></pre>
<br />
And to use this class in UserService :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">public class UserService {
@Inject
private UserEventDao userEventDao;
@Inject
private DateUtility dateUtility;
public void createCreationUserEvent(User user) {
UserEvent event = new UserEvent();
event.setUser(user);
event.setUserEventType(UserEventType.CREATION);
event.setEventDate(dateUtility.getCurrentDate());
userEventDao.create(event);
}
}
</code></pre>
<br />
Then we can mock the call to DateUtility in our unit test :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
@InjectMocks
private UserService userService;
@Mock
private UserEventDao userEventDao;
@Mock
private DateUtility dateUtility;
@Test
public void createCreationUserEvent_withCorrectParameters_shouldCreateAnEvent() {
// Given
User user = new User();
Date eventDate = new Date();
doReturn(eventDate).when(dateUtility).getCurrentDate();
// When
userService.createCreationUserEvent(user);
// Then
UserEvent expectedUserEvent = new UserEvent();
expectedUserEvent.setUser(user);
expectedUserEvent.setUserEventType(UserEventType.CREATION);
expectedUserEvent.setEventDate(eventDate);
verify(userEventDao).create(expectedUserEvent);
verifyNoMoreInteractions(userEventDao);
}
}
</code></pre>
<br />
Like in the Powermock solution, you can now be sure that your test will be in success at each execution.<br />
<br />
<h2>
<span style="font-size: x-large;">Conclusion : Powermock vs DateUtility</span></h2>
Firstly, these two solutions can be applied to every other case where you have to manipulate dates. It works all the time.<br />
<br />
The advantage of the Powermock solution is that you don't have to modify your business code to write a good test. However, the bytecode manipulation done by the framework is expensive in term of execution time : in my environment, this simple test needs about 400 ms to be executed whereas the test with Mockito needs almost 0 ms. On more complicated tests with several static classes mocked, this execution time can be even worst : I already saw entire tests classes executed in almost 8 seconds, which can be very problematic in term of productivity.<br />
<br />
Concerning the DateUtility solution, I think it is elegant to have a class with the responsability to manipulate dates. With that, you shouldn't have any "new Date()" call or Calendar manipulation in other classes. And of course, the main bonus is that you can write a good unit test. I would so recommend you this solution!<br />
<br />
I hope you enjoyed this thread and I would be very glad to hear which others tricks do you use to test your dates. Also, I you encounter a case where the DateUtility solution cannot help you, I would love to help you.<br />
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0tag:blogger.com,1999:blog-4318921998224788049.post-34288856540632822013-06-13T22:26:00.002+02:002013-06-19T20:53:43.622+02:00Tips to write maintainable unit testsBecause unit tests are considered by a lot of developers as a waste of time, they make no efforts to write a code of quality. It is perhaps not a problem for the developer to understand his tests while he is developing his feature. But when he needs to modify the feature behavior and therefore his tests several weeks later, he will encounter some difficulties to understand his own tests and it will be expensive to modify them.<br />
<br />
However it is easy to write a readable and maintainable unit test. You just have to follow several tips. <br />
<br />
<h2>
<span style="font-size: large;">Tip #1 : give a comprehensive name to each test</span></h2>
Your unit test name should be comprehensive enough to understand which method you are testing and which case you are testing. To achieve that, three information are mandatory in your method name :<br />
<ul>
<li>the name of the tested method</li>
<li>the input parameters of the tested method</li>
<li>what is the expected behavior of the tested method</li>
</ul>
If you respect this rule, it is not mandatory anymore to write other documentation (like javadoc in Java). The method name is comprehensive enough. For example : <br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> @Test
public void filterInvalidUsers_withAnInvalidUser_shouldReturnAnEmptyList() {
}
</code></pre>
<br />
Ok I understand you can be chocked to see an underscore in the method name. But it is a little trick to improve the visibility of what your test is doing. Moreover you can easily identify if you have forgotten a case using the method listing of your favorite IDE : <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-q5BjdJglveg/UbnnSyLWLmI/AAAAAAAAAA0/AHjSp3tIQKk/s1600/Capture.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-q5BjdJglveg/UbnnSyLWLmI/AAAAAAAAAA0/AHjSp3tIQKk/s1600/Capture.PNG" /></a></div>
<br />
<ul>
</ul>
<h2>
<span style="font-size: large;">Tip #2 : use Given / When / Then template</span></h2>
Every unit test has three sections of instructions :<br />
<ul>
<li>the test preparation</li>
<li>the call to the tested method</li>
<li>the assertions</li>
</ul>
In some unit tests, it is not always obvious to find these sections. So a little trick to easily identify them is to comment the beginning of each section. "Given" for the test preparation, "When" for the call to the tested method, and "Then" for the assertions. Doing that for each of yours unit tests give a kind of template easily understandable by every developer. For example :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">@Test
public void filterInvalidUsers_withAnInvalidUser_shouldReturnAnEmptyList() {
// Given
User user = new User();
user.setName("userName");
user.setBirthDate(new Date());
user.setStatus(-1);
// When
List<User> users = userService.filterInvalidUsers(</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;">Arrays.asList(user)</code>);
// Then
assertNotNull(users);
assertTrue(users.isEmpty());
}
</code></pre>
<br />
<h2>
<span style="font-size: large;">Tip #3 : use reusable methods for the Given and Then sections </span></h2>
Even if it is logical to factorize your code, this basic coding principle is not always applied to the unit tests. Some developers prefer to copy & paste an entire unit test, just change the test name and one of the assertions, whereas they could create an utility method used by several tests.<br />
<br />
An exemple of factorization of our test method could be :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">@Test
public void filterInvalidUsers_withAnInvalidUser_shouldReturnAnEmptyList() {
// Given
User user = createUserWithCorrectParameters();
user.setStatus(-1);
// When
List<User> users = userService.filterInvalidUsers(</code><code style="color: black; word-wrap: normal;"><code style="color: black; word-wrap: normal;">Arrays.asList(user)</code>);
// Then
assertThatThereIsNoUser(users);
}
private User createUserWithCorrectParameters() {
User user = new User();
user.setName("userName");
user.setBirthDate(new Date());
user.setStatus(10);
return user;
}
private void assertThatThereIsNoUser(List<user><User> users) {
assertNotNull(users);
assertTrue(users.isEmpty());
}
</user></code></pre>
<h2>
<span style="font-size: large;"> </span></h2>
<h2>
<span style="font-size: large;">Tip #4 : mock all interactions to other objects </span></h2>
An important principle of the unit tests is to test only the behavior of the tested class, and not the behavior of all the objects used by this class. If you don't respect this principle, you could have impacts on several test classes each time you change the behavior of a single class, which is a waste of time.<br />
<br />
To do that you can mock every other objects used by the tested class. Mock an object means simulate his behavior and not do a real call to the object. Great Java librairies allow to do this job, for example Easymock or Mockito. I will do a comparative of these two solutions in another thread.<br />
<h2>
<span style="font-size: large;"> </span></h2>
<h2>
<span style="font-size: large;">Tip #5 : use partial mocking to have smaller tests </span></h2>
Imagine you want to add a createUsers method in the same class of the filterInvalidUsers method :<br />
<br />
<pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;">public void createUsers(List<User><user> users) {
List<user> validUsers = filterInvalidUsers(users);
if (! validUsers.isEmpty()) {
userDao.createUsers(validUsers);
}
}</user></user></code></pre>
<br />
You have already written your unit tests on filterInvalidUsers and you don't want to write them again. How can you avoid that? A solution is to use partial mocking. Whereas classic mocking allows you to mock the objects used in your tested class, the partial mocking allows you to mock a method in the class you are testing. So you can simulate the call to the method then verify that the call has been performed. For example, in the createUsers unit tests, you can mock the call to the filterInvalidUsers. In Java, EasyMock and Mockito both allow to do partial mocking.<br />
<h2>
<span style="font-size: x-large;"> </span></h2>
<h2>
<span style="font-size: x-large;">Conclusion</span></h2>
I hope theses few tips will help you to write more maintainable unit tests. Don't hesitate to share your own techniques by commenting this thread. I would be very glad to share with you.<br />
<h2>
</h2>
<br />Jean-Sébastien FRANCKhttp://www.blogger.com/profile/07246386252869234767noreply@blogger.com0