sábado, noviembre 27, 2010

La Mer Au Ciel d'Ete Confond


Have you ever started a war with java.util.Calendar class? I hope no, but unfortunately a lot of developed applications deals with Date and Time. Calendar class is a singleton class for manage time, but one never knows exactly how to work with it. You know you have methods for add and subtract time, but nothing more. Now I ask, How about periods of time, durations, intervals...? you need to develop a class for managing all these requirements. Let's take an example:

Imagine we want to compare if current date is Christmas Date.

Calendar compares all its fields, so year, month, day, minutes, seconds, ... are compared, but wait, Christmas day does not depend on year, every year has its Christmas Day. So a "Util" class is developed for treating with this situation. This class would look something like:

Calendar christmasDay = Calendar.getInstance();
christmasDay.set(MONTH, 12);
christmasDay.set(DATE, 25);

Calendar now = Calendar.getInstance();
if(christmasDay.get(MONTH)==now.get(MONTH) && christmasDay.get(MONTH)==now.get(DATE)) {
return true;
} else {
return false;
}

Next example:

Boulangerie example. Our brother-in-law have a bakery, and wants to automatize oven, so a each bread stays only 15 minutes inside oven (of course not all breads gets into oven at same time). With Calendar/Date class this logic would look something like:

static long FIFTEEN_MINUTES = 15 * 60 * 1000;

long startOvenTimeBread1 = System.currentTimeInMillis();
//Thread Polling
....
long currentTime = System.currentTimeInMillis();

if(currentTime>=startOvenTimeBread1 + FIFTEEN_MINUTES) {
//bread ok
}

As you can see first example it is tedious because you must get all required attributes, and compare them with current time. In second example is not as hard as first but you lost the sensation of working with time, all are longs, and the only way to understand what is each long is with variable name.

So Yes working with Dates/Times in JDK looks like a suicide, but here comes JSR-310 (JDK-8?included).

Let's rewrite the same examples but with JSR-310 API.

Christmas Day example:

MonthDay christmasDay = MonthDay.of(12, 25);
MonthDay now = MonthDay.nowSystemClock();

boolean isChristmas = now.equals(christmasDay);

I think more readable than using Calendars, no problem with hours, minutes, seconds, years, ... MonthDay class manages all. These kind of classes in JSR-310 are known as Partials because they only represents partial time.

Now Boulangerie needs our help. As you have observed, the first problem in that case is that you only manages longs, and as a developer you don't have the feeling of working with time. Also an ugly comparison is required.

How make code looking better:

Duration duration = Duration.ofStandardMinutes(15);

Instant insertedBreadInstant = Instant.nowSystemClock();
Instant bakedBreadInstant = insertedBreadInstant.plus(duration);

//Thread Polling
...
boolean isBaked = bakedBreadInstant.isBefore(Instant.nowSystemClock());


Well more instructions than long approach are used, but don't you think that this implementation is easily read, and you know what is each field.

So don't you think JSR-310 specification and RI implementation is a really useful API? more powerful than current Calendar/Date classes.

Now it is only time to wait for being built-in JDK, but of course you can download RI implementation from java.net.

martes, noviembre 23, 2010

... Una Vez Está Bien Pero Dos Está Mal ...

Hamcrest is about making your test readable. Hamcrest is a framework that allows us to use object matchers for developing stylized sentences in assertions.

Let's look some examples:

Why write this:

double calculatedValue = 1000D;
...
assertEquals(1000, calculatedValue, 0)


When you can write this:

assertThat(calculatedValue, is(1000));

Or Why write off all this code:

List validResults = Arrays.asList("4+", "3+", "2+");
String result = .....;

assertFalse(validResults.contains(result));


When you can write:

assertThat("1+", not(isIn(validResults)));

Three points should get your attention:

I. Using assertEquals (in our case with double) is not readable, what is the meaning of third parameter in previous example ?¿ What's happen if instead of 0 I pass a 1?¿.

II. When you speak yourself repeating a requirement, you say: "calculated value should be 1000", but it is rarely common to think "1000 should be my calculated value". Moreover, for example String assertEquals method contains 2 parameters, which of two are the expected value and current value?

III. Flexible notation in contrast to JUnit notation. For specifying that an element cannot be into a collection, in Junit assertFalse is used, and that's sounds strange using with a contains method of a list you can misunderstood exactly what are being test. Look Hamcrest expression, no possible error, we are asserting that "1+" not in validResults. Hamcrest has a module for treating collections expressions, like anyOf, isOneOf, hasItem, hasItems...

But wait, there is more, you can also build you own Hamcrest Matcher. A matcher is the responsible for validating if something is what expected. Hamcrest comes with a lot of built-in Matchers, but what's happen if we need one new matcher? Let's look an example:

Hamcrest does NOT comes with File matchers. We need to validate if after our business logic, a file is created in a directory.

- In Junit like expressions, would look something like:

final File createdFile = new File("test1");
assertTrue(createdFile.exists());


- In Hamcresst with built-in matchers:

final File createdFile = new File("test1");
assertThat(createdFile.exists(), is(true));


Maybe better than JUnit but expressiveness may be missing.

- What about that:

final File createdFile = new File("test1");
assertThat(createdFile, exists());


Better I think.

But a Matcher for asserting that, does not exists, so let's write one:

public class ExistsFileMatcher extends TypeSafeMatcher {

@Override
public void describeTo(final Description description) {
description.appendText("does not exists.");
}

@Override
public boolean matchesSafely(final File expectedFile) {
return expectedFile.exists();
}

@Factory
public static Matcher exists() {
return new ExistsFileMatcher();
}

}

One will agree that is so simple, only one method that validates the expression, other one that describes the error, and finally a factory.

And this matcher can be used in conjunction with other matchers. It is also valid that one:

final File createdFile = new File("test1");
assertThat(createdFile, not(exists()));


And that's Hamcrest, don't you think is a better way to express assertions rather than plain JUnit?

domingo, noviembre 14, 2010

What Colour is God? Why Can't You Tell Me.

Usually one tend to think about testing as a residual part of development. Nothing further from reality. Testing, so JUnit classes, needs to be treated as "business code". Must follow naming conventions, code must be sectioned, and of course must be readable. For this reason, John Ferguson Smart has its own rules that I would like to summarize and comment:

As John Ferguson Smart told in his entries, there are 5 rules that all JUnit classes must follows, that rules are:

I. Don't say "test", say "should".


JUnit test should pass, but "should pass", maybe there is something wrong in classes that makes test fail, for this reason we are writing off tests. Also, test cases must be validated with requirements, because a requirement is specified as should/shall not with test, same nomenclature is used. Thanks of this improvement, it is easy validating requirements using tests.

Also as Neal Ford suggests each word of your test method name must be separated with '_' character. I find this suggestion so useful because you don't need to use your mind for parsing camel case methods. Of course this only applies to JUnit methods (Yes the only exception between delivery classes and test classes). With this small change your test department will be thankful how fast can validate requirements.

For example the method: testTransfer must be changed to transferShouldDeductSumFromSourceAccountBalance as John Smart suggests, but another change is better, and is changing to: transfer_Should_Deduct_Sum_From_Source_Account_Balance and that's more readable.

II. Don't test classes, test their behavior.


Of course because test cases are related to use cases, tests must validate behavior. This also affects on avoiding testing your classes for the only porpouse of reaching 100% of coverage. 100% of coverage is important, but guarantees nothing, if you don't keep in mind that you must test behavior.

III. Test class names are important too.


Of course when you want to locate where some behavior is tested, it helps the name of your test. Class names must answer the question: When is this behavior applicable? as method names should answer the question: What behavior are we testing?.

What you think is better test class name: TransactionTest or WhenMoneyTransactionIsCommited. Both test names are valid but second one is descriptive about what behavior are you testing.

IV. Structure your tests well.


Test classes are also business classes and for that reason they also have the right to be refactored, kept clean and readable. For that reason is so important to write tests consistently. A really good suggestion is divide each test in three main sections, Given-When-Then. Only with tabs and CR can work perfectly, but you can also use frameworks like JBehave or Easyb.

V. Test are deliverables too.


Tests must be treated as commercial code, not because your client does not see test classes, you can ignore them from static analysis.

Donate If You Can and Find Post Useful