Unit testing involves writing individual units of code to determine if they are functioning properly, where a unit is the smallest testable part like a method or class; it ensures code is properly designed and functions as intended when client uses the interface, but should not rely on external resources or other subsystems to avoid integration testing. Unit testing finds bugs early, allows for safe refactoring, and improves code quality and developer focus through documentation and feedback from running tests automatically.
2. What is Unit Testing
Unit test is a method by which individual units of source code
are tested to determine if they are fit for use.
A unit is the smallest testable part of an application like
method or class.
Unit tests are written from a programmer’s perspective.
They ensures that a particular method of a class successfully
performs a set of operations.
Unit testing drives design: developer is concerned about how
client will use the interface of the class that is under the test.
3. What is not for?
It's not for testing correct inter-operation of multiple
subsystems.
NOTE: It should be a stand-alone test which is not related
to other subsystems.
It should not rely on external resources like(RDBMS,LDAP etc).
NOTE: Introducing dependencies on external resources or
data turns unit tests into integration tests.
In many cases you can't write a unit test to reproduce bug
appeared in production.
NOTE: it's not regression testing. with unit test you can
check logic responsible for interaction between different
layers.
4. The advantages of Unit Testing
● Ensure code continues to work as intended(only if it has a
good coverage)
● Safe refactoring. Allows refactoring without fear to break the
code.
● Fewer bugs. Find some tricky bugs in logic on first stage
NOTE: tricky unit test scenarios may prevent many bugs.
● Developer concentrates more on the code and design.
NOTE: less build/deploy cycles to see some progress.
green bar shows you the progress
● Documentation
5. Disadvantages of Unit testing
Big time investment. For the simple case you lose about 20%
of the actual implementation, but for complicated cases you lose
much more.
NOTE: If you correctly follow TDD it will going to save you
time later in long-term perspective!
Design Impacts. Sometimes the high-level design is not clear
at the start and evolves as you go along - this will force you to
completely redo your test which will generate a big time lose.
NOTE: postpone unit tests in this case until you have
clarified high-level design.
6. Best Practices
● Make sure your tests test one thing and one thing only.
● Readability is important for tests. (see Example)
● Each unit test should be independent from the other.
● Separate you concerns. Extract layers to improve the
design. (see Example with DAO layer)
● Mock behavior with mocks to concentrate on test
scenario.
● Test Coverage(Check coverage during testing)
● Tests should run automatically to provide continuous
feedback.
Keep the bar green to keep the code clean!
7. Bad practices
● A singleton gets implemented using a static method. Static
methods are avoided by people who do unit testing
because they cannot be mocked or stubbed.
Static methods are death to unit testability ref
● Don't rely on external resources
● Do not test the GUI.
8. JUnit - Adding new test case
Mark your test cases with @Test annotations.
Use @Before and @After to run method before and after
every test case. Several tests need similar objects created
before they can run. (See Example)
Use @BeforeClass and @AfterClass to run for one time
before and after all test cases.(Use it only to share expensive
setup)
Static imports makes code more readable: (See Example)
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
@RunWith(MockitoJUnitRunner.class)
9. JUnit test structure
Make test readable. Use next pattern:
// Given
Create some objects required for testing.
// When
Execute method that is under test
// Then
Check state and verify interaction
See example
10. JUnit - Assertion
1. Choose correct assert method from org.junit.Assert.*:
assertNull, assertNotNull, assertTrue, assertEquals...
2. Keep it simple:
assertEquals(age, calculateAge(dob)); // bad practice
assertEquals(age, 25); // Best practice. Keep it simple
3. Use overloaded method with argument for message:
assertNull("Value must be null in case of error", value);
11. JUnit - test under construction
Sometimes we need to temporarily disable a test that is
under construction. Use @Ignore annotation on method
or class to achieve it. Specify the reason why it's ignored.
@Ignore("Enable when TASK-2 is implemented")
public class MyClassTest {
@Test public void testThat1() { ... }
@Test public void testThat2() { ... }
}
public class MyClassTest {
@Ignore("Gotta go now! will fix it later.")
@Test void testThat1() { ... }
}
12. JUnit - Test Case With Exception
1. Expect that exception is thrown: (See example) @Test
(expected=EmailExistException.class) public void
testExceptionIsThrown() { ... }
2. More flexible old school way:
try { // execute method under the test customerService.add
(customer);
fail("exception must be thrown");
} catch (ServiceException exception) {
// state assertion: check error code
assertEquals(exception.getCode(), 404);
}
3. JUnit 4.7 has @Rule ExpectedException
13. State vs Interaction testing
State testing asserts properties on an object
Example: Verify that after tested method execution object has
properties: assertEquals(2, item.getCount());
Interaction testing verifies the interactions between objects.
Example: Did my controller correctly call my services in
specified order? Can be used only with mocked objects.
Mockito is a framework for interactions testing.
Mockito is a mocking framework
that tastes really good!
14. Mock behavior with mocks.
Mocks or mock objects simulate the behavior of complex, real
(non-mock) objects and are therefore useful when a real object
is impractical or impossible to incorporate into a unit test.
They provide:
● Default values unless stubbed
● Can help verify interactions, order of interaction, method
parameters etc.
Mockito is a Java based mocking framework that allows you to
write beautiful tests with clean & simple API.
15. Why Mockito is good?
● with Mockito, you only stub what you need and go on
happily completing the functionality. No need to stub
everything.
● simple and clean syntax
● all required documentation can be found in javadocs to org.
mockito.Mockito.
● Has an ability to create partial mocks.
● It tastes really good
16. JUnit - Mock in Mockito
This will mock all methods in MyService class and provide
default values:
1. @Mock MyService myService;
All classes with @Mock annotation will be injected to
@InjectMocks class by type automatically when using
@RunWith(MockitoJUnitRunner.class).
2. You can mock and inject services manually:
MyService myService = mock(MyService.class);
ServiceUnderTest service = new ServiceUnderTest();
service.setMyService(myService);
You can mock concrete classes, not only interfaces!
17. JUnit - Stubbing in Mockito
Stubbing is adding canned response to Mock object methods.
Examples:
● method stubbing:
when(mockService.someMethod(”value”))
.thenReturn(”someValue”);
● stubbing method with exception:
when(mockService.someMethod("value"))
.thenThrow(new MyException("Error"));
or stubbing void method with exception:
doThrow(new MyException(”Error”)
.when(mockService).someMethod("value");
Mockito verifies argument values in natural java style: by using
an equals() method.
18. JUnit - Argument matching in Mockito
Sometimes, when extra flexibility is required then you might
use argument matchers:
● when(mockedList.get(anyString())).thenReturn("element");
● any(ClassName.class)
● Custom argument matcher:(see ArgumentMatcher)
IMPORTANT: If you are using argument matchers, all
arguments have to be provided:
verify(mock).someMethod(anyInt(), anyString(), eq("arg"));
verify(mock).someMethod(anyInt(), anyString(), "arg") -
Wrong
19. JUnit - Verify behavior in Mockito
Once created, mock will remember all interactions. Then you
can selectively verify whatever interaction you are interested in:
● verify(mockService).someMethod("someArgs");
● verify(mockService, times(2)).someMethod("someArgs");
● verify(mockService, never()).someMethod("someArgs");
NOTE: never() is an alias to times(0)
● atLeastOnce()
● atLeast(2)
● atMost(5)
● verifyZeroInteractions(mockService)
● verifyNoMoreInteractions(mockService)
20. JUnit - Verification in order
Verification in order is flexible - you don't have to verify all
interactions one-by-one but only those that you are
interested in testing in order.
//create inOrder object passing any mocks relevent for in-order verification
InOrder inOrder = inOrder(firstMock, secondMock);
// make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
21. JUnit - Spy(Partial mocking) in Mockito
When you use the Spy then the real methods are called (unless
a method was stubbed).
● Calls real methods unless the method is stubbed.
● Use of too much spy is potential code smell.
MyService myService = new MyService();
MyService spy = spy(myService);
//optionally, you can stub out some methods when(spy.
someMethod()).thenReturn(val);
//real method logic will be executed
spy.realMethod();
See Example
22. Test Coverage (see Example)
Eclemma plugin for eclipse: http://www.eclemma.org/
#ant coverage - to generate report from console