Beruflich Dokumente
Kultur Dokumente
O h no!
PART I
JUNIT AND TESTNG FOR ADVANCED GURU-TYPES - 2-13
PART II
FIGURING OUT WHAT TO TEST... - 14-16
PART III
SETTING UP MOCK ALTERNATE REALITIES - 17-20
PART IV
MOCKITO FOR ADVANCED USERS (WOOHOO!) - 21-28
So if you see something wrong, like the "check engine" light is flashing an Before we get into the blood and guts of the report, lets review a
angry red warning, it probably wouldn't be wise to start driving away. In a very brief list of points to remember:
similar manner unit tests act as warning signals that something does not
Unit tests are code themselves (so they might also have bugs, ha!)
work as expected. It wouldn't be wise to deploy code into production when
all your unit tests are failing. Unit tests perform a small task and then verify that the result is the
expected one
But unit tests are just that, early warnings and internal checks that your Unit tests that are failing are a warning signal that something is
code does what is expected. wrong with the expectations of the system
Unit tests that pass do not guarantee 100% correctness of the code.
Unit tests should be updated if the requirements change
JUnit is a port to Java from the original SUnit from Smalltalk created by Kent Beck. Since
then similar libraries exist for other language as well such as nUnit, phpUnit, Cunit. Here is a
comprehensive list of other testing frameworks
TestNG (test next generation) is another framework for Java, which can be seen as a more
advanced JUnit. It is also possible to use other test frameworks for languages that run on the
JVM to test Java code. Spock which is the test framework of Groovy can be used to test both Java
and Groovy code.
Note: Unfortunately, we have limited space in this report, so well ask you to view
certain parts of this content on the ZeroTurnaround blog. The following parts appear
in Why Your Next Cloud App Will Probably Suck Without.Unit Testing:
Get started by setting up a Hello World! unit test, will all the code
you need to make it happen
Seeing your unit tests from the end-user perspective, driven by the
features required.
Unit testing as a warning signal for later (i.e. what happens we a new
dev joins your team in 2 years and starts playing with legacy code?)
However, lets continue with some more advanced stuff for those of you who already checked
out the blog. Please note that, unfortunately, some code snippets are too long to fit in one
column, and therefore carry over to the next. We are planning a fix for this in the future,
apologize for this inconvenience.
@Test There is clearly a lot of code duplication here. JUnit has a @Before
public void test1() { annotation for code that runs automatically before each test method. So
MyUriValidator myValidator = new MyUriValidator();
your test can be simplified to:
myValidator.allowFileUrls(true);
myValidator.allowInternationlizedDomains(false);
myValidator.allowReservedDomains(false); public class MyUriValidatorTest {
myValidator.allowCustomPorts(true);
private MyUriValidator myValidator = null;
assertTrue("Domain is valid",myValidator.
isValidUrl("http://www.google.com")); @Before
} public void beforeEachTest() { //Name of method does not
actually matter
@Test myValidator = new MyUriValidator();
public void test2() { myValidator.allowFileUrls(true);
MyUriValidator myValidator = new MyUriValidator(); myValidator.allowInternationlizedDomains(false);
myValidator.allowFileUrls(true); myValidator.allowReservedDomains(false);
myValidator.allowInternationlizedDomains(false); myValidator.allowCustomPorts(true);
myValidator.allowReservedDomains(false); }
myValidator.allowCustomPorts(true);
@Test
assertTrue("Domain is valid",myValidator. public void test1() {
isValidUrl("file://home/users")); assertTrue("Domain is valid",
} myValidator.isValidUrl("http://www.
google.com"));
You use the @After annotation to clear up resources requested in the @Test
@Before. Here is an example for an integration test where a database public void test2() {
System.out.println("Test 2 runs");
connection is created in the beginning and released in the end for EACH myDbConnection.doSomethingElseWithDb();
test. //Assert statements should be here for this test to be complete
}
import org.junit.After; }
import org.junit.Before;
import org.junit.Test; If you run this test it will print the following (broken due to space issues):
public class SimpleDatabaseTest { Preparing db Connection Test 2 runs
Cleaning up Preparing db Connection
private MyDbConnection myDbConnection= null;
Test 1 runs Cleaning up
@BeforeClass Notice that the annotated methods for @BeforeClass and @AfterClass
public static void prepareSimulation() { are declared static (this is a limitation of JUnit, but TestNG is more
// Will run only once regardless the number of tests
System.out.println("Preparing earthquake
flexible). If you run this unit test you will get:
simulation");
earthQuakeSimulator = new EarthQuakeSimulator(); Preparing earthquake simulation
earthQuakeSimulator.startup(); Test 1 runs
}
Test 2 runs
@AfterClass Cleaning up
public static void endSimulation() {
// Will run only once regardless the number of tests
These annotations are also very useful when you use a specific helper for
System.out.println("Cleaning up");
earthQuakeSimulator.shutdown(); a test (e.g an integration framework or an in-memory database) that you
} want to use for all your tests.
@Test
@Rule
public Timeout timeout = new Timeout(100);
@Test
public void shouldAddItemsQuickly() {
weightCalculator.addItem(-5);
}
@Category(MyGroupA.class)
@Test
public void testSomething() { Multiple test arguments with JUnit theories
new FirstClass().testSomething();
} When you annotate the test method with @Theory keyword, then JUnit will
invoke this method with all possible combinations of variables declared as
@Category(MyGroupB.class)
@DataPoint (or @DataPoints, if you have packed the variables into an
@Test
public void testSomethingElse() { array).
new FirstClass().testSomethingElse();
}
For example, when we want to test some method which takes two integers
}
as parameters and we would like to run tests with such set of parameters: {
public class SecondClassTests { (0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)} then @Theory can help us to keep the
code compact.
@Category({MyGroupA.class, MyGroupC.class})
@Test
public void testSomething() { @RunWith(Theories.class)
new SecondClass().testSomething(); public class CombinationTest {
}
} @DataPoints public static int[] INTS = new int []{0, 1, 2, 3};
@Theory
Next, when we want to run all test methods belonging to group
public void testCalculation(int x, int y) {
MyGroupA, we need to create a test suite for that. Assume.assumeTrue(x < y);
new Combination().calculate(x, y);
@RunWith(Categories.class)
}
@IncludeCategory(MyGroupA.class)
}
Can run tests of the other library No Yes, TestNG can run JUnit tests
The third capability is very powerful but also needs great attention if @BeforeMethod
public void beforeEachTest() {
you choose to use it. Normally, all your test cases should be completely myValidator = new MyUriValidator();
independent and they should be able to execute in any order. No test myValidator.allowFileUrls(true);
should have side effects, and no test should depend on the outcome of myValidator.allowInternationlizedDomains(false);
myValidator.allowReservedDomains(false);
another one. myValidator.allowCustomPorts(true);
}
But lets look at the first two features we mentioned above:
Parameterized and Parallel testing @DataProvider
private static final Object[][] createSamples() {
return new Object[][] { { "http://www.google.com",
SUPER-POWERED PARAMETERIZED TESTS WITH TESTNG true },
While JUnit has basic support for parameterized tests, the approach it { "file://home/users", true },
{ "http://localhost:8080/", false } };
follows is not very flexible. As you saw in the MyUrlValidator example, }
parameters exist as private fields in your test class, and also a special
constructor is needed. @Test(dataProvider = "createSamples")
public void testURL(String uriTested, boolean
expectedResult) {
Wouldnt it be great to add parameters as arguments into the test method
assertEquals(myValidator.isValidUrl(uriTested),
themselves without polluting the test class with extra fields? Of course it expectedResult,
would! This is exactly what TestNG does. "Test for " + uriTested);
}
TestNG introduces the concept of a Dataprovider, which is a way to create
test data and bind it to test methods. Binding happens via the name of the }
data provider, and the test data and test code are completely decoupled
and you are free to connect them as you want. This offers much more
flexibility than JUnit.
With a big number of parameters, error reporting is somewhat lacking in TestNG also supports having the DataProvider and the test method in
JUnit. JUnit will number each test argument in sequence, giving no direct completely different classes, allowing for even better code reuse.
indication what was the argument. If we create a failed test where only one
argument did not pass you get the following: Here is the DataProvider class on its own:
In a big enterprise project, it might be very helpful to centralize all test data
in a single place for easier management of new requirements that affect
those test cases that have yet to be verified.
Lets see what you should NOT test and get it out of the way first:
So what happens if you get across a legacy application with no unit tests? What if the business logic part ends up being
thousands of lines of code? Where do you start?
In this case you should prioritize things a bit and just write tests for the following:
How much of the code in these areas should we test, you might ask. Well, now that we know which areas to focus on, we can now
start to analyze just how much testing we need to feel confident about our code.
Note: Due to space limitations, well kindly ask you to visit the ZeroTurnaround blog if youd like to see more content
on Code Coverage, which is a metric that tells you how much of your code is touched by your unit testing efforts.
We covered this in Dont Test Blindly: The Right Methods for Unit Testing Your Java Apps.
For example, if you change something, how can you tell what will be affected? The side effects
of your changes should be clear from the beginning, and we do this by Mocking, which lets you
create alternative realities for your application to play through, without having any side effects or
consequences in reality (kinda like the Truman show :)
Note: We dont have all the space wed like to provide all the code examples for Mocking, so
please see the ZeroTurnaround blog post, How to mock up your Unit Test environment to
create alternate realities for details on:
For now, lets see why using a Mocking Framework is better than doing anything by hand, and some of
the tools available.
Mocking attempts to solve in an easy way the creation of fake objects that help the unit testing process. Like our comparison to the
Truman Show, mock objects are designed to fool a Java object to think that it communicates with other real objects.
From the point of view of the tested class (i.e. the FinalInvoiceStep of the example provided in the blog post), everything runs
normally. It communicates with other objects and gets all the appropriate responses. Behind the scenes however, this is only theater, a
mummers show if you will, which has been carefully planned by you.
We suggest using mocking when the class you want to unit test
Ok, that list is a bit morbid, but you get the point. You can also use
communicates with other classes that have side effects which should only
mocking in other cases as well. A common practice is to mock some
be called in the real production system. Some random examples, with
classes that are very slow and based on a lot of dependencies not needed
significant side effects in real life, are:
for the unit test. Mocking is also great when you want to emulate strange
errors such as a full hard disk, network failure, wrong serialized version
Charging a credit card or bank account
etc.
Printing medical records, invoices, personal documents, etc.
Therefore you should not use mocking when you are interested in:
For all these cases you need integration tests, because we are now looking beyond the classes themselves. If you have several objects that work correctly
isolated (because the unit tests pass), this does not mean that they will also work correctly when used together. This is where integration tests come in.
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
I first started using Mockito in a big legacy code base with
lots of DaoFactories, ObjectManagers, Useless delegates
and other unncessery stuff. It allowed me to create
simple, fast unit tests that focused on one thing and one
thing only. The integration tests took hours (and nobody
paid attention to them in the team). But unit tests with
Mockito finished in seconds and after a while when most
of the team embraced them we would catch regression
errors before sending code to production.
Kostis Kapelonis
RebelLabs Content Producer
Lets look at EmailUniqueValidator, its just a normal ConstraintValidator which you can
think of as a util class that queries some service to base assumptions on the data. Theres
nothing special here; it includes a method isValid, which contains non-trivial logic to test.
@Test
public void testNonUniqueEmailIsNotValid() {
String emailValue = "root@example.com";
UserService userService = mock(UserService.class);
User user = mock(User.class);
// we expect findByEmail be called
when(userService.findByEmail(emailValue)).thenReturn(user);
boolean valid = new EmailUniqueValidator().setUserService(userService).
isValid(emailValue, null);
assertFalse("email is valid despite there's a user with it", valid);
}
Naturally, there are integrations with frameworks; when you use a iceberg, we showed how easy it can be to integrate your project, your
dependency injection framework, like Spring in the example above, you existing tests and Mockito.
UserDetailWrapper has one method with logic: getAuthorities(). So imagine that you cannot mock User. Thats when
Mockitos spies come to the rescue. Instead of mocking an object and stubbing its interactions with the environment, a spy
wraps the object and delegates method calls to the real instance while providing all the monitoring features of a mock.
@Test
public void getAuthoritiesNoContactUser() {
// a real object is created and real object method will be called
User user = spy(new User());
UserDetailsWrapper wrapper = new UserDetailsWrapper(user);
Collection<? extends GrantedAuthority> authorities = wrapper.getAuthorities();
assertTrue("User has no contact, but has authorities!", authorities.isEmpty());
verify(user).getFacebookId();
verify(user).getEmail();
// verify id is not necessary if we have no facebook and no email
verify(user, never()).getId();
}
However, a spy allows us to verify that the Facebook and email getters of the underlying user object
were called and id getter wasnt. Isnt that great? Now you can build and verify your assumptions of
what the system is doing without modifying its functionality.
@Test
public void normalUserHasAuthorities() {
// a real object is created and a real object method will be called
User realInstance = getComplexInitializedUser();
User user = spy(realInstance);
UserDetailsWrapper wrapper = new UserDetailsWrapper(user);
Collection<? extends GrantedAuthority> authorities = wrapper.getAuthorities();
verify(user).getFacebookId();
verify(user).getEmail();
verify(user).getId();
assertTrue("User has no id, but has authorities!", !authorities.isEmpty() && authorities.
iterator().next().getAuthority().equals(StandardAuthorities.USER));
}
private User getComplexInitializedUser() {
User user = new User();
user.setId(1L);
user.setEmail("root@example.com");
return user;
}
Above, we can see that a User comes with a state and is initialized and ready. Still we are able to peek at the
wrapper functionality flow and verify the return values and the underlying component usage.
Now spies offer little added value compared to mocks when a system has a nice, testable design, but when its
is something you dont want to look at design wise, they are really handy. Just dont allow them to get you too
deep and start testing objects youre spying on.
For example, you want to fetch a record from the database first and then Simply put, Mockito is a very rich framework and offers many useful
check its data, not the other way around. Mockito offers you an InOrder features, and when you are armed with the ones we showed above, you
interface. can build readable tests for systems of any complexity. Have fun!
Keeping Your Code Safe with Unit Testing introduced the concept
of unit testing and the reasons why you and your development team
should be using these methods and tools.