Art of unit testing: How developer should care about code quality
-
Upload
dmytro-patserkovskyi -
Category
Engineering
-
view
1.663 -
download
0
Transcript of Art of unit testing: How developer should care about code quality
DIVE INTO THE HISTORY
Debugging
4
Automated Testing
Manual Testing
SUnit(1989)JUnit(2001)
1970’s1950’s
DIVE INTO THE HISTORY5
Kent Beck: 1989: "Simple Smalltalk Testing: With Patterns"
”
Software testing it the complex of techniques include the process of executing a program or application with the intent of
finding software bugs.
6
UNIT TESTS
10
Unit tests – part of code that are designed to ensure the smallest divisible pieces of code (units or components) are working the way they were intended.
UNIT TESTS@BeforeClass
public void testBeforeSuite() {
prepareMailService();
}
@Test(groups = "mail")
public void testEmailService() {
MailService service= generateMailService();
Assert.assertNotNull(service);
}
@Test(dependsOnMethods = { "testMailService" }, groups="mail")
public void testSending() {
MailService service= generateMailService();
Assert.assertTrue(service.sendMail(subject, message));
}
11 Test
Configuration
Preparations
Assertions
Grouping
UNIT TESTS
Test - part of code, that check logic of another part of code.
Assertions - mechanism for checking results of lome logic, defines if our unit test will be success or failed.
Configuration - all attributes and properties of test.
Preparations - part of code, that prepares state and context for our tests.
12
UNIT TESTS
Grouping - associates unit tests into groups and suites. Each group or suite should have common property or characteristic.
13
GROUP SUITE
Contains Methods
Depends on Groups
Annotations Config
Runs Separately
Contains Groups
Flex Configuring
Configuration in XML
Runs Separately
UNIT TESTS
Test Dependencies. If a dependent method is fail, all the subsequent test methods will be skipped, NOT failed.
@Test(groups = "mail")
public void testEmailService() {
MailService service= generateMailService();
Assert.assertNotNull(service);
}
@Test(dependsOnMethods = { "testMailService" }, groups="mail")
public void testSending() {
MailService service= generateMailService();
Assert.assertTrue(service.sendMail(subject, message));
}
15
UNIT TESTS
Parameterized Tests. Run your single test for different data sets.
@DataProvider
public Object[][] smtpHosts() {
return new Object[][]{ {"smtp.host1"}, {"mail.host2"} };
}
@Test(dataProvider = "smtpHosts", groups="mail")
public void testSending(String host) {
MailService service= generateMailService(host);
Assert.assertTrue(service.sendMail(subject, message));
}
16
UNIT TESTS
Rich @Before and @After configuration.
@BeforeSuite - @AfterSuite
@BeforeTest - @AfterTest
@BeforeGroups - @AfterGroups
@BeforeClass - @AfterClass
@BeforeMethod - @AfterMethod
17
PRINCIPLES OF UNIT TESTS
19
Principles of Unit Tests:
▹ Easy and fast to run▹ Unit▹ Simple▹ Independent▹ Checking all cases▹ Isolate
ARCHITECTURE OF PROJECT21
Let’s imagine component for sending emails, and try to cover it with unit tests.
Services:
▹ MailService - service for sending emails. Based on Javax Mail.▹ LogsStorage - service for collecting logs about mails sending and flushing it
to persistent storage.▹ LogsMailService - service for work with emails and logs.
PRINCIPLES OF UNIT TESTS
// Bad
@Test(groups = "mail")
public void testEmailSending() {
MailService service= generateMailService();
Assert.assertNotNull(service);
MailBuilder builder= new MailBuilder();
Mail mail = new MailBuilder().
.newMail()
.addSubject("my mail")
.addRecipient(firstRecipient)
.addRecipient(secondRecipient)
.build();
Assert.assertNotNull(mail);
Assert.assertEquals(EXPECT, mail.getSubject());
...
Assert.assertTrue(service.sendMail(mail));
}
22
// Good
@BeforeTest
public void initialize() {
service= generateMailService();
}
@Test(groups = "mail")
public void testEmailSending() {
Assert.assertTrue(
service.sendMail(prepareMail())
);
}
Unit & Simple - each test covers one piece of code.
PRINCIPLES OF UNIT TESTS
// Bad@Test(groups = "mail")
public void testEmailLogsSuccessful() {
mailLogsStorage.logGood();
Assert.assertEquals(1, mailLogsStorage.getCountGood());
Assert.assertEquals(1, mailLogsStorage.getCountAll());
}
@Test(groups = "mail")
public void testEmailLogsFailed() {
mailLogsStorage.logBad();
Assert.assertEquals(1, mailLogsStorage.getCountBad());
Assert.assertEquals(2, mailLogsStorage.getCountAll());
}
23
// Good@Test(groups = "mail")
public void testEmailLogsSuccessful() {
mailLogsStorage.logGood();
Assert.assertEquals(1, mailLogsStorage.getCountGood());
Assert.assertEquals(1, mailLogsStorage.getCountAll());
}
@Test(groups = "mail")
public void testEmailLogsFailed() {
mailLogsStorage.logBad();
Assert.assertEquals(1, mailLogsStorage.getCountBad());
Assert.assertEquals(1, mailLogsStorage.getCountAll());
}
@AfterMethodpublic void cleanAfter() {
mailLogsStorage.cleanState();
}
Independent - test should runs with random order and in parallel.
PRINCIPLES OF UNIT TESTS
// Bad
@BeforeTest
public void initialize() {
service= generateMailService();
}
@Test(groups = "mail")
public void testEmailSending() {
Assert.assertTrue(
service.sendMail(prepareGoodMail())
);
}
24
// Good
@BeforeTest
public void initialize() {
service= generateMailService();
}
@Test(groups = "mail")
public void testEmailSendingSuccess() {
Assert.assertTrue(
service.sendMail(prepareGoodMail())
);
}
@Test(groups = "mail")
public void testEmailSendingFailed() {
Assert.assertFalse(
service.sendMail(prepareBadMail())
);
}
Checking all cases - tests for success case is not enough.
PRINCIPLES OF UNIT TESTS
// Bad
@Before
public void before() {
service = new LogMailService();
service.setMailService(new MailService());
}
@Test(groups = "mail")
public void testEmailService() {
Assert.assertTrue(service.sendMail(mail));
}
25
// Good
@Before
public void before() {
service = new LogMailService();
}
@Test(groups = "mail")
public void testEmailService() {
MailService mock = mock(MailService.class)
when(mock.sendMail(mail))
.thenReturn(true);
service.setMailService(mock)
Assert.assertTrue(service.sendMail(mail));
times(1, mock.sendMail(mail));
}
Isolate - encapsulated logic should be covered with separated tests.
ISOLATION PHILOSOPHY
26
Mocking - creating objects that simulate the behaviour of real objects to isolate part of code for unit testing.
ISOLATION PHILOSOPHY28
Mocks - simulated objects that mimic the behavior of real objects in controlled ways.
Partial mocks - object, created as shell for real one, when you need to stub several methods of an object leaving the remainder free to respond to calls
normally.
ISOLATION PHILOSOPHY29
@Before
public void before() {
service = new LogMailService();
}
@Test
public void testEmailService() {
MailService mock = mock(MailService.class)
when(mock.sendMail(mail))
.thenReturn(true);
service.setMailService(mock)
Assert.assertTrue(service.sendMail(mail));
times(1, mock.sendMail(mail));
}
Mocks
ISOLATION PHILOSOPHY30
@Test
public void testLogsRefresh() {
LogsStorage storageSpy = spy(logsStorage);
doReturn(true).when(storageSpy).flush();
storageSpy.refresh();
Assert.assertEquals(1, storageSpy.getCountAll());
times(1, storageSpy.flush());
}
Partial Mocks
ISOLATION PHILOSOPHY
What if… we have private factory method?
Class MailService {
...
private Session createMailSession() {
...
}
}
32
ISOLATION PHILOSOPHY
Use dark power of
33
OR.. create a new class and move all private methods to this as
public. Use dependency injection and mocking. This can force you to
use an unwanted design. Private methods are not an option.
ISOLATION PHILOSOPHY
What if… we have calls to static method of framework?
Class MailService {
...
public Session sendMail() {
...
// Send message Transport.send(message);
}
}
34
ISOLATION PHILOSOPHY
Use dark power of
35
OR... wrap all static method calls in a separate class and use
dependency injection to mock this object. This will create an extra
layer of unnecessary classes in your application. However, this can
of course be useful if you want to encapsulate the framework to be
able to replace it.
ISOLATION PHILOSOPHY
PowerMock for legacy code with antipatterns.Mockito is enough for well constructed architecture.
36