Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Tomek Kaczanowski - Practical Unit Testing with JUnit and Mockito - 2013.pdf
Скачиваний:
228
Добавлен:
07.03.2016
Размер:
6.59 Mб
Скачать

Chapter 6. Things You Should Know

thing to happen. The second option is to reduce the asynchronous code to its synchronous counterpart and then solve it using a standard approach.

If you are interested in testing asynchronous and concurrent code then [goetz2006] is probably the book for you to read.

6.10. Testing Thread Safe

Chuck Norris can test multi-threaded applications with a single thread.

— Wisdom of the Internet ;)

In the real world, the applications we write are often accessed simultaneously. We use specialized tools (e.g. JMeter17) to make sure that our software can handle this concurrent load. Sometimes it is important that we verify such requirements, even as they relate to cases involving single classes. This section gives an example of such a test.

6.10.1. ID Generator: Requirements

Let us implement a utility class whose task will be to generate unique identifiers (IDs). Its interfaces are shown in Listing 6.29.

Listing 6.29. ID generator interface

public interface IdGenerator {

/**

* @return unique id */

Long nextId();

}

6.10.2. ID Generator: First Implementation

The first, rather naive implementation is shown in Listing 6.30.

Listing 6.30. ID generator implemented using the System.currentTimeMillis method

public class SystemIdGenerator implements IdGenerator {

public Long nextId() {

return System.currentTimeMillis();

}

}

The SystemIdGenerator class uses a static method of the System class to obtain unique identifiers.

To test if the returned IDs are really unique, we need to call the nextId() method of our ID generator two times in a row, and compare the results. Such a test is shown in Listing 6.31.

17http://jakarta.apache.org/jmeter/

126

Chapter 6. Things You Should Know

Listing 6.31. Basic test of an ID generator

public class SystemIdGeneratorTest {

private IdGenerator idGen = new SystemIdGenerator();

@Test

public void idsShouldBeUnique() { Long idA = idGen.nextId(); Long idB = idGen.nextId();

assertNotEquals("idA " + idA + " idB " + idB, idA, idB);

}

}

Even though some time passes between the generation of idA and idB this test fails every time I execute it. This means that both of the identifiers generated by an instance of the SystemIdGenerator class are equal. Apparently, current CPUs are fast enough to execute the nextId() method two times in every millisecond. However, looking at the test code, I think that in some circumstances the test might pass. That would be something extremely unwelcome. A test which sometimes passes and sometimes fails (for no apparent reason) is called a "flickering test". In general, flickering tests are a nightmare to deal with. One time they pass, the next they fail, etc., and there are no clues we can follow up that might shed light on the reason for this non-deterministic behaviour.

Having said that, I do promise we will write a much better test soon. But for now, let us make the ID generator class better, so that it passes the test.

6.10.3. ID Generator: Second Implementation

Another version of an ID generator - the AtomicIdGenerator class shown in Listing 6.32 - uses a unary increment operator. Now it should always return unique values.

Listing 6.32. ID generator implemented using a unary increment operator

public class AtomicIdGenerator implements IdGenerator {

private static Long nextId = System.currentTimeMillis();

public Long nextId() { return nextId++;

}

}

If we run our test in Listing 6.31 against this version of an ID generator, the test will pass. No matter how many times we execute the test, no matter if we add a for loop inside the test code to repeat it again and again, it will pass. Yet, it does not mean that our generator class is ready to be used in the real world. In the next step we will prove it is still not robust enough.

If you expect your code to be used concurrently, then your tests should simulate this.

Even if a unary increment operator were atomic (which it isn’t!), it might happen that two threads both execute the nextId() method and get the same value. How? Well, two threads might obtain the same "old" value of the nextId variable, then both might increment it and subsequently assign a "new" value to the idA and idB variables. To write a test which exposes such a threat, we would have to create two

127

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]