SlideShare a Scribd company logo
1 of 79
Download to read offline
Bad Tests, Good Tests

Tomek Kaczanowski




                        http://twitter.com/#!/devops_borat
Tomek Kaczanowski
• Developer
• Team lead
• Blogger
   • http://kaczanowscy.pl/tomek
• Book author
   • http://practicalunittesting.com

• Working at CodeWise (Krakow, Poland)
   • ...we are hiring, wanna join us?
                                         JUnit version
                                         coming soon!
Why bother with tests?


• System works as expected



• Changes do not hurt



• Documentation
Tests help to achieve quality




                     Not sure when I saw this picture – probably
                     in GOOS?
What happens if we do it wrong?
• Angry clients
• Depressed developers




                         http://www.joshcanhelp.com
When I started out with unit tests, I was
enthralled with the promise of ease and
security that they would bring to my
projects. In practice, however, the
theory of sustainable software through
unit tests started to break down. This
difficulty continued to build up, until I
finally threw my head back in anger and
declared that "Unit Tests have become
more trouble than they are worth."
               Llewellyn Falco and Michael Kennedy, Develop Mentor August 09
http://chrispiascik.com/daily-drawings/express-yourself/
write the right test
write the right test




write this test right
Before we begin

• All of the examples are real but were:
   • obfuscated
       • to protect the innocents :)
   • truncated
       • imagine much more complex domain objects
• Asking questions is allowed
   • ...but being smarter than me is not ;)
We don't need no stinkin' asserts!
public void testAddChunks() {
    System.out.println("*************************************");
    System.out.println("testAddChunks() ... ");
    ChunkMap cm = new ChunkMap(3);
    cm.addChunk(new Chunk("chunk"));

    List testList = cm.getChunks("chunk",null);

    if (testList.isEmpty())
        fail("there should be at least one list!");
    Chunk chunk = cm.getActualChunk("chunk",null);
    if (chunk.getElements().isEmpty())
        fail("there should be at least one element!");
    if (cm.getFinalChunkNr() != 1)
        fail("there should be at least one chunk!");
    // iterate actual chunk
    for (Iterator it = chunk.getElements().iterator();
               it.hasNext();) {
        Element element = (Element) it.next();
        System.out.println("Element: " + element);
    }
    showChunks(cm);
    System.out.println("testAddChunks() OK ");
}                                                  Courtesy of @bocytko
Success is not an option...

  /**
    * Method testFailure.
    */
  public void testFailure() {
       try {
           Message message = new Message(null,true);
           fail();
       } catch(Exception ex) {
           ExceptionHandler.log(ExceptionLevel.ANY,ex);
           fail();
       }
  }




                                                  Courtesy of @bocytko
What has happened? Well, it failed...
public void testSimple() {
    IData data = null;
    IFormat format = null;
    LinkedList<String> attr = new LinkedList<String>();
    attr.add("A");
    attr.add("B");

    try {
        format = new SimpleFormat("A");
        data.setAmount(Amount.TEN);
        data.setAttributes(attr);
        IResult result = format.execute();
        System.out.println(result.size());
        Iterator iter = result.iterator();
        while (iter.hasNext()) {
            IResult r = (IResult) iter.next();
           System.out.println(r.getMessage());
        ...
    }
    catch (Exception e) {
        fail();
    }
}
                                                   Courtesy of @bocytko
What has happened? Well, it failed...
public void testSimple() {
    IData data = null;
    IFormat format = null;
    LinkedList<String> attr = new LinkedList<String>();
    attr.add("A");
    attr.add("B");   data is null - ready or not,
                    NPE is coming! 
    try {
        format = new SimpleFormat("A");
        data.setAmount(Amount.TEN);
        data.setAttributes(attr);
        IResult result = format.execute();
        System.out.println(result.size());
        Iterator iter = result.iterator();
        while (iter.hasNext()) {
            IResult r = (IResult) iter.next();
           System.out.println(r.getMessage());
        ...
    }
    catch (Exception e) {
        fail();
    }
}
                                                   Courtesy of @bocytko
No smoke without tests
class SystemAdminSmokeTest extends GroovyTestCase {

void testSmoke() {

    def ds = new org.h2.jdbcx.JdbcDataSource(
        URL: 'jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=Oracle',
        user: 'sa', password: '')

        def jpaProperties = new Properties()
        jpaProperties.setProperty(
            'hibernate.cache.use_second_level_cache', 'false')
        jpaProperties.setProperty(
            'hibernate.cache.use_query_cache', 'false')

        def emf = new LocalContainerEntityManagerFactoryBean(
           dataSource: ds, persistenceUnitName: 'my-domain',
           jpaVendorAdapter: new HibernateJpaVendorAdapter(
               database: Database.H2, showSql: true,
               generateDdl: true), jpaProperties: jpaProperties)

    …some more code below
}
No smoke without tests
class SystemAdminSmokeTest extends GroovyTestCase {

void testSmoke() {
// do not remove below code
// def ds = new org.h2.jdbcx.JdbcDataSource(
//     URL: 'jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=Oracle',
//     user: 'sa', password: '')
//
//     def jpaProperties = new Properties()
//     jpaProperties.setProperty(
//         'hibernate.cache.use_second_level_cache', 'false')
//     jpaProperties.setProperty(
//         'hibernate.cache.use_query_cache', 'false')
//
//     def emf = new LocalContainerEntityManagerFactoryBean(
//        dataSource: ds, persistenceUnitName: 'my-domain',
//        jpaVendorAdapter: new HibernateJpaVendorAdapter(
//            database: Database.H2, showSql: true,
//            generateDdl: true), jpaProperties: jpaProperties)

    …some more code below, all commented out :(
}
Let's follow the leader!

                  @Test
                  public class ExampleTest {

                      public void testExample() {
                         assertTrue(true);
                      }
                  }
Uh-oh, I feel lonely...

                   @Test
                   public class ExampleTest {

                          public void testExample() {
                             assertTrue(true);
                          }
                   }
Conclusions

• Automation!
   • Running
   • Verification
• Do not live with broken window
   • And remember there is no one else to fix them but
     you!
   • It is a full time job!
• You should be informed why your test failed
• Master your tools
   • …at least learn the basics!
Use of the real objects obscures the test
@Test
public void shouldGetTrafficTrend() {
        //given
        TrafficTrendProvider trafficTrendProvider
           = mock(TrafficTrendProvider.class);
        Report report = new Report(null, "", 1, 2, 3,
           BigDecimal.ONE, BigDecimal.ONE, 1);
        TrafficTrend trafficTrend = new TrafficTrend(report, report,
           new Date(), new Date(), new Date(), new Date());
        given(trafficTrendProvider.getTrafficTrend())
           .willReturn(trafficTrend);
        TrafficService service
           = new TrafficService(trafficTrendProvider);

        //when
        TrafficTrend result = service.getTrafficTrend();

        //then
        assertThat(result).isEqualTo(trafficTrend);
}
Use of the real objects obscures the test
@Test
public void shouldGetTrafficTrend() {
        //given
        TrafficTrendProvider trafficTrendProvider
           = mock(TrafficTrendProvider.class);
        TrafficTrend trafficTrend = mock(TrafficTrend.class);
        given(trafficTrendProvider.getTrafficTrend())
           .willReturn(trafficTrend);


        TrafficService service
            = new TrafficService(trafficTrendProvider);

        //when
        TrafficTrend result = service.getTrafficTrend();

        //then
        assertThat(result).isEqualTo(trafficTrend);
}
Mock'em All!
@Test
public void shouldAddTimeZoneToModelAndView() {
    //given
    UserFacade userFacade = mock(UserFacade.class);
    ModelAndView modelAndView = mock(ModelAndView.class);
    given(userFacade.getTimezone()).willReturn("timezone X");


    //when
    new UserDataInterceptor(userFacade)
        .postHandle(null, null, null, modelAndView);


    //then
    verify(modelAndView).addObject("timezone", "timezone X");
}
ModelAndView from
Mock'em All!                               SpringMVC – a mere
                                           container for data, without
@Test                                      any behaviour
public void shouldAddTimeZoneToModelAndView() {
    //given
    UserFacade userFacade = mock(UserFacade.class);
    ModelAndView modelAndView = mock(ModelAndView.class);
    given(userFacade.getTimezone()).willReturn("timezone X");


    //when
    new UserDataInterceptor(userFacade)
        .postHandle(null, null, null, modelAndView);


    //then
    verify(modelAndView).addObject("timezone", "timezone X");
}
Use the front door
@Test
public void shouldAddTimeZoneToModelAndView() {
    //given
    UserFacade userFacade = mock(UserFacade.class);
    ModelAndView modelAndView = new ModelAndView();
    given(userFacade.getTimezone()).willReturn("timezone X");


    //when
    new UserDataInterceptor(userFacade)
        .postHandle(null, null, null, modelAndView);
                                          a pseudocode but that is
                                          what we mean
    //then
    assertThat(modelAndView).contains("timezone", "timezone X");
}
Mock'em All!
Public class Util {

     public String getUrl(User user, String timestamp) {
     String name = user.getFullName();
     String url = baseUrl
          +"name="+URLEncoder.encode(name, "UTF-8")
          +"&timestamp="+timestamp;              Developer wants to check
     return url;
     }
                                                 whether timestamp is added
                                                to the URL when this method
     public String getUrl(User user) {          is used
     Date date = new Date();
     Long time = date.getTime()/1000; //convert ms to seconds
     String timestamp = time.toString();
     return getUrl(user, timestamp);
     }
}
Mock'em All!
Public class Util {

     public String getUrl(User user, String timestamp) {
     ...
     }

     public String getUrl(User user) {
     ...
     }
}

                   @Test
                   public void shouldUseTimestampMethod() {
                       //given
                       Util util = new Util();
                       Util spyUtil = spy(util);

                       //when
                       spyUtil.getUrl(user);

                       //then
                       verify(spyUtil).getUrl(eq(user), anyString());
                   }
Dependency injection
Use the front door                    will save us

@Test
public void shouldAddTimestampToGeneratedUrl() {
    //given
    TimeProvider timeProvider = mock(TimeProvider.class);
    Util util = new Util(timeProvider);
    when(timeProvider.getTime()).thenReturn("12345");
    util.set(timeProvider);


    //when
    String url = util.getUrl(user);


    //then
    assertThat(url).contains("timestamp=12345");
}
Single Responsibility Principle


 A test should have one and only one reason to fail.
Testing two things at once
@DataProvider
public Object[][] data() {
    return new Object[][] { {"48", true}, {"+48", true},
         {"++48", true}, {"+48503", true}, {"+4", false},
         {"++4", false}, {"", false},
         {null, false}, {" ", false}, };
}

@Test(dataProvider = "data")
public void testQueryVerification(String query, boolean expected) {
    assertEquals(expected, FieldVerifier.isValidQuery(query));
}
Testing two things at once
@DataProvider
public Object[][] data() {
    return new Object[][] { {"48", true}, {"+48", true},
         {"++48", true}, {"+48503", true}, {"+4", false},
         {"++4", false}, {"", false},
         {null, false}, {" ", false}, };
}

@Test(dataProvider = "data")
public void testQueryVerification(String query, boolean expected) {
    assertEquals(expected, FieldVerifier.isValidQuery(query));
}

          testQueryVerification1() {
              assertEquals(true, FieldVerifier.isValidQuery(„48”));
          }
          testQueryVerification2() {
              assertEquals(true, FieldVerifier.isValidQuery(„+48”));
          }
          testQueryVerification3() {
              assertEquals(true, FieldVerifier.isValidQuery(„++48”));
          }
          testQueryVerification4() {
              assertEquals(true, FieldVerifier.isValidQuery(„+48503”));
          }
          ...
Concentrate on one feature
@DataProvider
public Object[][] validQueries() {
return new Object[][] { {"48"}, {"48123"},
         {"+48"}, {"++48"}, {"+48503"}};
}

@Test(dataProvider = "validQueries")
public void shouldRecognizeValidQueries(String validQuery) {
    assertTrue(FieldVerifier.isValidQuery(validQuery));
}



@DataProvider
public Object[][] invalidQueries() {
  return new Object[][] {
    {"+4"}, {"++4"}, {""}, {null}, {"   "} };
}

@Test(dataProvider = "invalidQueries")
public void shouldRejectInvalidQueries(String invalidQuery) {
    assertFalse(FieldVerifier.isValidQuery(invalidQuery));
}
“And”
@Test
public void shouldReturnRedirectViewAndSendEmail() {
        //given
        given(bindingResult.hasErrors()).willReturn(false);
        given(userData.toEntity()).willReturn(user);
        given(userService.saveNewUser(eq(userData.toEntity())))
           .willReturn(user);

       //when
       ModelAndView userRegisterResult = userRegisterController
          .registerUser(userData, bindingResult, request);

       //then
       assertThat(userRegisterResult.getViewName())
          .isEqualTo("redirect:/signin");
       verify(mailSender).sendRegistrationInfo(user);
}
One feature at a time

@Test
public void shouldRedirectToSigninPageWhenRegistrationSuceeded () {
    ...
}

@Test
public void shouldNotifyAboutNewUserRegistration() {
    ...
}




          Hint: forget about methods
Readability is the king
Who the heck is “user_2” ?

@DataProvider
public static Object[][] usersPermissions() {
    return new Object[][]{
         {"user_1", Permission.READ},
         {"user_1", Permission.WRITE},
         {"user_1", Permission.REMOVE},
         {"user_2", Permission.WRITE},
         {"user_2", Permission.READ},
         {"user_3", Permission.READ}
    };
}
Ah, logged user can read and write...

@DataProvider
public static Object[][] usersPermissions() {
    return new Object[][]{
         {ADMIN, Permission.READ},
         {ADMIN, Permission.WRITE},
         {ADMIN, Permission.REMOVE},
         {LOGGED, Permission.WRITE},
         {LOGGED, Permission.READ},
         {GUEST, Permission.READ}
    };
}
domain1, domain2, domain3, ...
domain1, domain2, domain3, ...
domain1, domain2, domain3, ...
Do not make me learn the API!

  server = new MockServer(responseMap, true,
              new URL(SERVER_ROOT).getPort(), false);
Do not make me learn the API!

   server = new MockServer(responseMap, true,
               new URL(SERVER_ROOT).getPort(), false);




private static final boolean RESPONSE_IS_A_FILE = true;
private static final boolean NO_SSL = false;


server = new MockServer(responseMap, RESPONSE_IS_A_FILE,
                new URL(SERVER_ROOT).getPort(), NO_SSL);
Do not make me learn the API!

  server = new MockServer(responseMap, true,
              new URL(SERVER_ROOT).getPort(), false);




  server = createFileNonSSLMockServer(responseMap);
Do not make me learn the API!

  server = new MockServer(responseMap, true,
              new URL(SERVER_ROOT).getPort(), false);




  server = new MockServerBuilder()
     .withResponse(responseMap)
     .withResponseType(FILE)
     .withUrl(SERVER_ROOT)
     .withoutSsl().create();


  server = MockServerBuilder
     .createFileNoSSLServer(responseMap, SERVER_ROOT);
Naming is really important
Test methods names are important

• When test fails
• Relation to focused tests
Test methods names are important



  @Test
  public void testOperation() {
      configureRequest("/validate")
      rc = new RequestContext(parser, request)
      assert rc.getConnector() == null
      assert rc.getOperation().equals("validate")
  }
“should” is better than “test”


•   testOperation()
•   testQuery()
•   testConstructor()
•   testFindUsersWithFilter()


•   shouldRejectInvalidRequests()
•   shouldSaveNewUserToDatabase()
•   constructorShouldFailWithNegativePrice()
•   shouldReturnOnlyUsersWithGivenName()
“should” is better than “test”

• Starting test method names
  with “should” steers you in
  the right direction.
                                    http://jochopra.blogspot.com/




• “test” prefix makes your test
  method a limitless bag
  where you throw everything
  worth testing


                                  http://www.greenerideal.com/
Test methods names are important
@Test
public void testQuery(){
    when(q.getResultList()).thenReturn(null);
    assertNull(dao.findByQuery(Transaction.class, q, false));
    assertNull(dao.findByQuery(Operator.class, q, false));
    assertNull(dao.findByQuery(null, null, false));

    List result = new LinkedList();
    when(q.getResultList()).thenReturn(result);
    assertEquals(dao.findByQuery(Transaction.class, q, false), result);
    assertEquals(dao.findByQuery(Operator.class, q, false), result);
    assertEquals(dao.findByQuery(null, null, false), null);

    when(q.getSingleResult()).thenReturn(null);
    assertEquals(dao.findByQuery(Transaction.class, q, true).size(), 0);
    assertEquals(dao.findByQuery(Operator.class, q, true).size(), 0);
    assertEquals(dao.findByQuery(null, null, true), null);

    when(q.getSingleResult()).thenReturn(t);
    assertSame(dao.findByQuery(Transaction.class, q, true).get(0), t);
    when(q.getSingleResult()).thenReturn(o);
    assertSame(dao.findByQuery(Operator.class, q, true).get(0), o);
    when(q.getSingleResult()).thenReturn(null);
    assertSame(dao.findByQuery(null, null, true), null);
}
Test methods names are important
@Test
public void shouldReturnNullListWhenDaoReturnsNull {
    when(q.getResultList()).thenReturn(null);
    assertNull(dao.findByQuery(Transaction.class, q, false));
    assertNull(dao.findByQuery(Operator.class, q, false));
    assertNull(dao.findByQuery(null, null, false));
}
public void shouldReturnEmptyListWhenDaoReturnsIt {
    List result = new LinkedList();
    when(q.getResultList()).thenReturn(result);
    assertEquals(dao.findByQuery(Transaction.class, q, false), result);
    assertEquals(dao.findByQuery(Operator.class, q, false), result);
    assertEquals(dao.findByQuery(null, null, false), null);
}
public void shouldReturnNullSingleResultWhenDaoReturnsNull {
    when(q.getSingleResult()).thenReturn(null);
    assertEquals(dao.findByQuery(Transaction.class, q, true).size(), 0);
    assertEquals(dao.findByQuery(Operator.class, q, true).size(), 0);
    assertEquals(dao.findByQuery(null, null, true), null);
}
public void shouldReturnSingleResultReturnedByDao {
    when(q.getSingleResult()).thenReturn(t);
    assertSame(dao.findByQuery(Transaction.class, q, true).get(0), t);
    when(q.getSingleResult()).thenReturn(o);
    assertSame(dao.findByQuery(Operator.class, q, true).get(0), o);
    when(q.getSingleResult()).thenReturn(null);
    assertSame(dao.findByQuery(null, null, true), null);
}
Assertion part is freaking huge
public void shouldPreDeployApplication() {
     // given
     Artifact artifact = mock(Artifact.class);
     when(artifact.getFileName()).thenReturn("war-artifact-2.0.war");
     ServerConfiguration config
          = new ServerConfiguration(ADDRESS, USER, KEY_FILE, TOMCAT_PATH, TEMP_PATH);
     Tomcat tomcat = new Tomcat(HTTP_TOMCAT_URL, config);
     String destDir = new File(".").getCanonicalPath() + SLASH + "target" + SLASH;
     new File(destDir).mkdirs();

    // when
    tomcat.preDeploy(artifact, new FakeWar(WAR_FILE_LENGTH));

    //then
    JSch jsch = new JSch();
    jsch.addIdentity(KEY_FILE);
    Session session = jsch.getSession(USER, ADDRESS, 22);
    session.setConfig("StrictHostKeyChecking", "no");
    session.connect();

    Channel channel = session.openChannel("sftp");
    session.setServerAliveInterval(92000);
    channel.connect();
    ChannelSftp sftpChannel = (ChannelSftp) channel;

    sftpChannel.get(TEMP_PATH + SLASH + artifact.getFileName(), destDir);
    sftpChannel.exit();

    session.disconnect();

    File downloadedFile = new File(destDir, artifact.getFileName());

    assertThat(downloadedFile).exists().hasSize(WAR_FILE_LENGTH);
}
Just say it
public void shouldPreDeployApplication() {
   // given
   Artifact artifact = mock(Artifact.class);
   when(artifact.getFileName())
       .thenReturn(ARTIFACT_FILE_NAME);
   ServerConfiguration config
       = new ServerConfiguration(ADDRESS, USER,
          KEY_FILE, TOMCAT_PATH, TEMP_PATH);
   Tomcat tomcat = new Tomcat(HTTP_TOMCAT_URL, config);

    // when
    tomcat.preDeploy(artifact, new FakeWar(WAR_FILE_LENGTH));

    // then
    SSHServerAssert.assertThat(ARTIFACT_FILE_NAME)
       .existsOnServer(tomcat).hasSize(WAR_FILE_LENGTH);
}
Just say it
public void shouldPreDeployApplication() {
   // given
   Artifact artifact = mock(Artifact.class);
   when(artifact.getFileName())
       .thenReturn(ARTIFACT_FILE_NAME);
   ServerConfiguration config
       = new ServerConfiguration(ADDRESS, USER,
          KEY_FILE, TOMCAT_PATH, TEMP_PATH);
   Tomcat tomcat = new Tomcat(HTTP_TOMCAT_URL, config);

    // when   WHY NOT CREATE   WHY NOT USE
    tomcat.preDeploy(artifact, new FakeWar(WAR_FILE_LENGTH));
       A PRIVATE ASSERTION METHOD?
    // then
    assertThatFileIsOnServer(ARTIFACT_FILE_NAME,
            Tomcat, WAR_FILE_LENGTH);
}
Asserting using private methods
@Test
public void testChargeInRetryingState() throws Exception {
    // given
TxDTO request = createTxDTO(RequestType.CHARGE);
AndroidTransaction androidTransaction = ...
    request.setTransaction(androidTransaction);

   // when
   final TxDTO txDTO = processor.processRequest(request);

    // then
List<AndroidTransactionStep> steps
    = new ArrayList<>(androidTransaction.getSteps());
AndroidTransactionStep lastStep = steps.get(steps.size() - 1);
    assertEquals(lastStep.getTransactionState(), CHARGED_PENDING);
assertEquals(txDTO.getResultCode(), CHARGED);
}
Asserting using private methods
@Test
public void testChargeInRetryingState() throws Exception {
    // given
TxDTO request = createTxDTO(RequestType.CHARGE);
AndroidTransaction androidTransaction = ...
    request.setTransaction(androidTransaction);

   // when WHY NOT CREATE
   final TxDTO txDTO = processor.processRequest(request);
    A PRIVATE ASSERTION METHOD?
    // then
List<AndroidTransactionStep> steps
    = new ArrayList<>(androidTransaction.getSteps());
AndroidTransactionStep lastStep = steps.get(steps.size() - 1);
    assertEquals(lastStep.getTransactionState(), CHARGED_PENDING);
assertEquals(txDTO.getResultCode(), CHARGED);
}
Asserting using private methods
@Test
public void testChargeInRetryingState() throws Exception {
    // given
TxDTO request = createTxDTO(RequestType.CHARGE);
AndroidTransaction androidTransaction = ...

      // when
      final TxDTO txDTO = processor.processRequest(request);

      // then
      assertState(request, androidTransaction,
         CHARGED, CHARGE_PENDING, AS_ANDROID_TX_STATE,
              ClientMessage.SUCCESS, ResultCode.SUCCESS);
}
Matchers vs. private methods
assertState(TxDTO txDTO, AndroidTransaction androidTransaction,
    AndroidTransactionState expectedAndroidState,
       AndroidTransactionState expectedPreviousAndroidState,
       ExtendedState expectedState,
       String expectedClientStatus, ResultCode expectedRequestResultCode) {
    final List<AndroidTransactionStep> steps
    = new ArrayList<>(androidTransaction.getTransactionSteps());
    final boolean checkPreviousStep = expectedAndroidState != null;
    assertTrue(steps.size() >= (checkPreviousStep ? 3 : 2));

     if (checkPreviousStep) {
         AndroidTransactionStep lastStep = steps.get(steps.size() - 2);
         assertEquals(lastStep.getTransactionState(),
          expectedPreviousAndroidState);
     }

     final AndroidTransactionStep lastStep = steps.get(steps.size() - 1);
     assertEquals(lastStep.getTransactionState(), expectedAndroidState);
     assertEquals(lastStep.getMessage(), expectedClientStatus);

     assertEquals(txDTO.getResultCode(), expectedRequestResultCode);
     assertEquals(androidTransaction.getState(), expectedAndroidState);
     assertEquals(androidTransaction.getExtendedState(), expectedState);

     if (expectedClientStatus == null) {
         verifyZeroInteractions(client);
     }
}
Matchers vs. private methods

@Test
public void testChargeInRetryingState() throws Exception {
    // given
    TxDTO request = createTxDTO(CHARGE);
    AndroidTransaction androidTransaction = ...
    // when
    final TxDTO txDTO = processor.processRequest(request);
    // then
    assertThat(androidTransaction).hasState(CHARGED)
        .hasMessage(ClientMessage.SUCCESS)
        .hasPreviousState(CHARGE_PENDING)
        .hasExtendedState(null);
    assertEquals(txDTO.getResultCode(), ResultCode.SUCCESS);
}
Asserting implementation details

public void invalidTxShouldBeCanceled() {
    ...
    String fileContent =
          FileUtils.getContentOfFile("response.csv");
    assertTrue(fileContent.contains(
          "CANCEL,123,123cancel,billing_id_123_cancel,SUCCESS,"));
}
Asserting implementation details

public void invalidTxShouldBeCanceled() {
    ...
    String fileContent =
           FileUtils.getContentOfFile("response.csv");
    assertTrue(fileContent.contains(
           "CANCEL,123,123cancel,billing_id_123_cancel,SUCCESS,"));
}

public void invalidTxShouldBeCanceled() {
    ...
    String fileContent =
           FileUtils.getContentOfFile("response.csv");
    TxDTOAssert.assertThat(fileContent)
          .hasTransaction("123cancel").withResultCode(SUCCESS);
}
Know your tools
• Unit testing framework           • Additional libraries
    • Use of temporary file rule          • Hamcrest, FEST, Mockito,
     • Listeners                            catch-exception, awaitility,
                                            JunitParams, tempus-fugit, …
     • Concurrency
     • @Before/@After              • Build tool
                                       • Parallel execution
     • Parametrized tests
                                   • CI
     • Test dependencies
                                   • IDE
                                       • Templates
                                          • Shortcuts
Expected exceptions
@Test(expected=IndexOutOfBoundsException.class)
public void shouldThrowExceptionGettingElementOutsideTheList() {
    MyList<Integer> list = new MyList<Integer>();
    list.add(0);
    list.add(1);
    list.get(2);
}
Expected exceptions
@Test(expected=IndexOutOfBoundsException.class)
public void shouldThrowExceptionGettingElementOutsideTheList() {
    MyList<Integer> list = new MyList<Integer>();
    list.add(0);
    list.add(1);
    list.get(2);
}
                          http://code.google.com/p/catch-exception/
      @Test
      public void shouldThrowExceptionGettingtElementOutsideTheList() {
              MyList<Integer> list = new MyList<Integer>();
              list.add(0);
              list.add(1);
              catchException(list).get(2);
              assertThat(caughtException())
                   .isExactlyInstanceOf(IndexOutOfBoundsException.class);
      }
Expected exceptions (with catch-exception)

@Test
public void shouldThrowException() throws SmsException {


    catchException(gutExtractor)
        .extractGut(„invalid gut”);


    then(caughtException())
         .isInstanceOf(SmsException.class)
         .hasMessage("Invalid gut")
         .hasNoCause();
}

                               http://code.google.com/p/catch-exception/
Awaitility

  @Test
  public void updatesCustomerStatus() throws Exception {

      // Publish an asynchronous event:
      publishEvent(updateCustomerStatusEvent);

      // Awaitility lets you wait until
  // the asynchronous operation completes:
      await().atMost(5, SECONDS)
     .until(costumerStatusIsUpdated());
      ...
  }
                                   http://code.google.com/p/awaitility/
What do you really want to test?

  @Test
  public void shouldAddAUser() {
      User user = new User();
      userService.save(user);
      assertEquals(dao.getNbOfUsers(), 1);
  }
You wanted to see that the number increased

   @Test
   public void shouldAddAUser() {
        int nb = dao.getNbOfUsers();
        User user = new User();
        userService.save(user);
        assertEquals(dao.getNbOfUsers(), nb + 1);
   }

Because:
1) This is closer to what you wanted to test
2) There is no assumption about the database “users” table being empty
The dream of stronger, random-powered tests
public void myTest() {
   SomeObject obj = new SomeObject(
      randomName(), randomValue(), ....);
   // testing of obj here
}

Does it make your test stronger?
The dream of stronger, random-powered tests
public void myTest() {
   SomeObject obj = new SomeObject(
      randomName(), randomValue(), ....);
   // testing of obj here
}

Does it make your test stronger?
...or does it only bring confusion?
Test failed
Expected
   SomeObject(„a”, „b”, ....)
but got
   SomeObject(„*&O*$NdlF”, „#idSLNF”, ....)
Random done wrong
public void myTest() {
   SomeObject obj = new SomeObject(
           a, b, c, productCode());
      // testing of obj here
}

private String productCode(){
   String[] codes = {"Code A", "Code B",
                 "Code C", "Code D"};
   int index = rand.nextInt(codes.length);
   return codes[index];
}
Ceremony
@Test
public void shouldBeAdministrator() {
    //given
    User user = new Administrator();

    //when
    boolean administrator = user.isAdministrator()
    boolean advertiser = user.isAdvertiser();
    boolean domainer = user.isDomainer();

    //then
    assertThat(administrator).isTrue();
    assertThat(advertiser).isFalse();
    assertThat(domainer).isFalse();
}
Ceremony
@Test
public void shouldBeAdministrator() {
    User user = new Administrator();

    assertThat(user.isAdministrator()).isTrue();
    assertThat(user.isAdvertiser()).isFalse();
    assertThat(user.isDomainer()).isFalse();
}
Asking for troubles...
LoggingPropertyConfigurator configurator = mock(...);
BaseServletContextListener baseServletContextListener =
= new BaseServletContextListener(configurator)


@Test public void shouldLoadConfigProperties() {          Should load some
    baseServletContextListener.contextInitialized();      default config
    verify(configurator).configure(any(Properties.class));
}


@Test(expected = LoggingInitialisationException.class)
public void shouldThrowExceptionIfCantLoadConfiguration() Should load this
                                                           {
                                                            specific file
    System.setProperty("logConfig", "nonExistingFile");
    baseServletContextListener.contextInitialized();
}
Asking for troubles...
LoggingPropertyConfigurator configurator = mock(...);
BaseServletContextListener baseServletContextListener =
    = new BaseServletContextListener(configurator)

@Test public void shouldLoadConfigProperties() {
    baseServletContextListener.contextInitialized();
    verify(configurator).configure(any(Properties.class));
}

@Test(expected = LoggingInitialisationException.class)
public void shouldThrowExceptionIfCantLoadConfiguration() {
    System.setProperty("logConfig", "nonExistingFile");
    baseServletContextListener.contextInitialized();
}

@Before
public void cleanSystemProperties() {
    ...
}
Test-last? No!


• makes people not write tests at all
• makes people do only happy path testing
• tests reflect the implementation
Always TDD?

For six or eight hours spread over the next few weeks I
struggled to get the first test written and running. Writing
tests for Eclipse plug-ins is not trivial, so it’s not
surprising I had some trouble. [...] In six or eight hours
of solid programming time, I can still make significant
progress. If I’d just written some stuff and verified it by
hand, I would probably have the final answer to whether
my idea is actually worth money by now. Instead, all I
have is a complicated test that doesn’t work, a pile
of frustration, eight fewer hours in my life, and the
motivation to write another essay.
                                           Kent Beck, Just Ship, Baby
There is so much more to discuss…
• Integration / end-to-end tests which are not parametrized
  (so they all try to set up jetty on port 8080),
• Tests which should be really unit, but use Spring context
  to create objects,
• Tests with a lot of dependencies between them (a
  nightmare to maintain!),
• Tests which are overspecified and will fail whenever you
  touch the production code,
• Tests with monstrous objects-creation code,
• Tests which run slow,
• Tests which try to cover the deficiencies of production
  code and end up being a total mess,
• Tests which verify methods instead of verifying
  responsibilities of a class,
• Happy path tests,
• etc., etc.
Treat tests as the first class citizens
•    do it everyday or forget about it          •   make tests readable using matchers,
•    use the right tool for the job                 builders and good names
     •   and learn to use it!                   •   test behaviour not methods
•    do not live with broken windows            •   be pragmatic about the tests you write
•    respect KISS, SRP, DRY (?)                     •   TDD always?
•    write good code, and you will also write       •   what is the best way to test it?
     good tests                                         unit/integration/end-to-end ?
     •   or rather write good tests and you     •   automate!
         will get good code for free            •   always concentrate on what is worth
•    code review your tests                         testing
•    do more than happy path testing                •   ask yourself questions like: 'is it
                                                        really important that X should send
•    do not make the reader learn the API,              message Y to Z?'
     make it obvious
                                                •   use the front door – state testing before
•    bad names lead to bad tests                    interaction testing (mocks)
Thank you!
You can learn more about writing
high quality tests by reading my
book – „Practical Unit Testing”.

You can also participate in
writing of my new (free!) e-
book devoted to bad and good
tests.

More Related Content

What's hot

Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, howTomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, howTomasz Polanski
 
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)Danny Preussler
 
Important java programs(collection+file)
Important java programs(collection+file)Important java programs(collection+file)
Important java programs(collection+file)Alok Kumar
 
Introduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxIntroduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxDavid Rodenas
 
ES3-2020-07 Testing techniques
ES3-2020-07 Testing techniquesES3-2020-07 Testing techniques
ES3-2020-07 Testing techniquesDavid Rodenas
 
ES3-2020-06 Test Driven Development (TDD)
ES3-2020-06 Test Driven Development (TDD)ES3-2020-06 Test Driven Development (TDD)
ES3-2020-06 Test Driven Development (TDD)David Rodenas
 
Introduction to Software Testing
Introduction to Software TestingIntroduction to Software Testing
Introduction to Software TestingSergio Arroyo
 
201913046 wahyu septiansyah network programing
201913046 wahyu septiansyah network programing201913046 wahyu septiansyah network programing
201913046 wahyu septiansyah network programingwahyuseptiansyah
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleAnton Arhipov
 
The Ring programming language version 1.10 book - Part 17 of 212
The Ring programming language version 1.10 book - Part 17 of 212The Ring programming language version 1.10 book - Part 17 of 212
The Ring programming language version 1.10 book - Part 17 of 212Mahmoud Samir Fayed
 
ReactJS for Programmers
ReactJS for ProgrammersReactJS for Programmers
ReactJS for ProgrammersDavid Rodenas
 
Some testing - Everything you should know about testing to go with @pedro_g_s...
Some testing - Everything you should know about testing to go with @pedro_g_s...Some testing - Everything you should know about testing to go with @pedro_g_s...
Some testing - Everything you should know about testing to go with @pedro_g_s...Sergio Arroyo
 
Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript TestingKissy Team
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingDavid Rodenas
 
JEEConf 2017 - The hitchhiker’s guide to Java class reloading
JEEConf 2017 - The hitchhiker’s guide to Java class reloadingJEEConf 2017 - The hitchhiker’s guide to Java class reloading
JEEConf 2017 - The hitchhiker’s guide to Java class reloadingAnton Arhipov
 
Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testingjeresig
 
TDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing TechniquesTDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing TechniquesDavid Rodenas
 
Java Bytecode for Discriminating Developers - JavaZone 2011
Java Bytecode for Discriminating Developers - JavaZone 2011Java Bytecode for Discriminating Developers - JavaZone 2011
Java Bytecode for Discriminating Developers - JavaZone 2011Anton Arhipov
 

What's hot (20)

Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, howTomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
Tomasz Polanski - Automated mobile testing 2016 - Testing: why, when, how
 
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
15 tips to improve your unit tests (Droidcon Berlin 2016 Barcamp)
 
Important java programs(collection+file)
Important java programs(collection+file)Important java programs(collection+file)
Important java programs(collection+file)
 
Introduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicoxIntroduction to web programming for java and c# programmers by @drpicox
Introduction to web programming for java and c# programmers by @drpicox
 
ES3-2020-07 Testing techniques
ES3-2020-07 Testing techniquesES3-2020-07 Testing techniques
ES3-2020-07 Testing techniques
 
GMock framework
GMock frameworkGMock framework
GMock framework
 
ES3-2020-06 Test Driven Development (TDD)
ES3-2020-06 Test Driven Development (TDD)ES3-2020-06 Test Driven Development (TDD)
ES3-2020-06 Test Driven Development (TDD)
 
JS and patterns
JS and patternsJS and patterns
JS and patterns
 
Introduction to Software Testing
Introduction to Software TestingIntroduction to Software Testing
Introduction to Software Testing
 
201913046 wahyu septiansyah network programing
201913046 wahyu septiansyah network programing201913046 wahyu septiansyah network programing
201913046 wahyu septiansyah network programing
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassle
 
The Ring programming language version 1.10 book - Part 17 of 212
The Ring programming language version 1.10 book - Part 17 of 212The Ring programming language version 1.10 book - Part 17 of 212
The Ring programming language version 1.10 book - Part 17 of 212
 
ReactJS for Programmers
ReactJS for ProgrammersReactJS for Programmers
ReactJS for Programmers
 
Some testing - Everything you should know about testing to go with @pedro_g_s...
Some testing - Everything you should know about testing to go with @pedro_g_s...Some testing - Everything you should know about testing to go with @pedro_g_s...
Some testing - Everything you should know about testing to go with @pedro_g_s...
 
Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testing
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving Testing
 
JEEConf 2017 - The hitchhiker’s guide to Java class reloading
JEEConf 2017 - The hitchhiker’s guide to Java class reloadingJEEConf 2017 - The hitchhiker’s guide to Java class reloading
JEEConf 2017 - The hitchhiker’s guide to Java class reloading
 
Understanding JavaScript Testing
Understanding JavaScript TestingUnderstanding JavaScript Testing
Understanding JavaScript Testing
 
TDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing TechniquesTDD CrashCourse Part5: Testing Techniques
TDD CrashCourse Part5: Testing Techniques
 
Java Bytecode for Discriminating Developers - JavaZone 2011
Java Bytecode for Discriminating Developers - JavaZone 2011Java Bytecode for Discriminating Developers - JavaZone 2011
Java Bytecode for Discriminating Developers - JavaZone 2011
 

Viewers also liked

Basic principles of chemotherapy
Basic principles of chemotherapyBasic principles of chemotherapy
Basic principles of chemotherapyAmna Medani
 
Trabajo de power point
Trabajo de power pointTrabajo de power point
Trabajo de power pointEduardo Guaman
 
Subscribers, fans and followers: The meaning of Like. Report 10 (Exact Target...
Subscribers, fans and followers: The meaning of Like. Report 10 (Exact Target...Subscribers, fans and followers: The meaning of Like. Report 10 (Exact Target...
Subscribers, fans and followers: The meaning of Like. Report 10 (Exact Target...Retelur Marketing
 
2013 Skoll World Forum Panel Presentation of the Social Progress Index Design
2013 Skoll World Forum Panel Presentation of the Social Progress Index Design2013 Skoll World Forum Panel Presentation of the Social Progress Index Design
2013 Skoll World Forum Panel Presentation of the Social Progress Index Designsocprog
 
Printed email of money laundering account
Printed email of money laundering accountPrinted email of money laundering account
Printed email of money laundering accountAngela Baxley
 
Under the Dome (of failure driven pipeline)
Under the Dome (of failure driven pipeline)Under the Dome (of failure driven pipeline)
Under the Dome (of failure driven pipeline)Maciej Lasyk
 
AWS Elastic Container Service
AWS Elastic Container ServiceAWS Elastic Container Service
AWS Elastic Container ServiceLadislav Prskavec
 
LinkedIn Pulse: Para que Serve?
LinkedIn Pulse: Para que Serve?LinkedIn Pulse: Para que Serve?
LinkedIn Pulse: Para que Serve?Enrico Cardoso
 
Bethany kludt: Nursing Facts
Bethany kludt: Nursing FactsBethany kludt: Nursing Facts
Bethany kludt: Nursing FactsBethany Kludt
 
Oxigenoterapia, Ventilação Mecânica, UPP
Oxigenoterapia, Ventilação Mecânica, UPPOxigenoterapia, Ventilação Mecânica, UPP
Oxigenoterapia, Ventilação Mecânica, UPPIvanilson Gomes
 
15 ways to use images, audio and video in E-Tests
15 ways to use images, audio and video in E-Tests15 ways to use images, audio and video in E-Tests
15 ways to use images, audio and video in E-TestsKiran Budhrani
 
Тарас Шевченко
Тарас ШевченкоТарас Шевченко
Тарас ШевченкоNick Valchuk
 
BSB SKY CITY NIGERIA DEVELOPMENT CLUB
BSB SKY CITY NIGERIA DEVELOPMENT CLUBBSB SKY CITY NIGERIA DEVELOPMENT CLUB
BSB SKY CITY NIGERIA DEVELOPMENT CLUBOsa Okundaye Odmd
 

Viewers also liked (20)

Basic principles of chemotherapy
Basic principles of chemotherapyBasic principles of chemotherapy
Basic principles of chemotherapy
 
Trabajo de power point
Trabajo de power pointTrabajo de power point
Trabajo de power point
 
Subscribers, fans and followers: The meaning of Like. Report 10 (Exact Target...
Subscribers, fans and followers: The meaning of Like. Report 10 (Exact Target...Subscribers, fans and followers: The meaning of Like. Report 10 (Exact Target...
Subscribers, fans and followers: The meaning of Like. Report 10 (Exact Target...
 
Making susanne
Making susanneMaking susanne
Making susanne
 
2013 Skoll World Forum Panel Presentation of the Social Progress Index Design
2013 Skoll World Forum Panel Presentation of the Social Progress Index Design2013 Skoll World Forum Panel Presentation of the Social Progress Index Design
2013 Skoll World Forum Panel Presentation of the Social Progress Index Design
 
Printed email of money laundering account
Printed email of money laundering accountPrinted email of money laundering account
Printed email of money laundering account
 
Under the Dome (of failure driven pipeline)
Under the Dome (of failure driven pipeline)Under the Dome (of failure driven pipeline)
Under the Dome (of failure driven pipeline)
 
Shutl
ShutlShutl
Shutl
 
AWS Elastic Container Service
AWS Elastic Container ServiceAWS Elastic Container Service
AWS Elastic Container Service
 
LinkedIn Pulse: Para que Serve?
LinkedIn Pulse: Para que Serve?LinkedIn Pulse: Para que Serve?
LinkedIn Pulse: Para que Serve?
 
Bethany kludt: Nursing Facts
Bethany kludt: Nursing FactsBethany kludt: Nursing Facts
Bethany kludt: Nursing Facts
 
REVISTA
REVISTAREVISTA
REVISTA
 
Oxigenoterapia, Ventilação Mecânica, UPP
Oxigenoterapia, Ventilação Mecânica, UPPOxigenoterapia, Ventilação Mecânica, UPP
Oxigenoterapia, Ventilação Mecânica, UPP
 
Diario di bordo
Diario di bordoDiario di bordo
Diario di bordo
 
Informatiethema's: Internet
Informatiethema's: InternetInformatiethema's: Internet
Informatiethema's: Internet
 
15 ways to use images, audio and video in E-Tests
15 ways to use images, audio and video in E-Tests15 ways to use images, audio and video in E-Tests
15 ways to use images, audio and video in E-Tests
 
leaflet prof
leaflet profleaflet prof
leaflet prof
 
Тарас Шевченко
Тарас ШевченкоТарас Шевченко
Тарас Шевченко
 
BSB SKY CITY NIGERIA DEVELOPMENT CLUB
BSB SKY CITY NIGERIA DEVELOPMENT CLUBBSB SKY CITY NIGERIA DEVELOPMENT CLUB
BSB SKY CITY NIGERIA DEVELOPMENT CLUB
 
Iso dinkes
Iso dinkesIso dinkes
Iso dinkes
 

Similar to 33rd Degree 2013, Bad Tests, Good Tests

2013 DevFest Vienna - Bad Tests, Good Tests
2013 DevFest Vienna - Bad Tests, Good Tests2013 DevFest Vienna - Bad Tests, Good Tests
2013 DevFest Vienna - Bad Tests, Good TestsTomek Kaczanowski
 
GeeCON 2012 Bad Tests, Good Tests
GeeCON 2012 Bad Tests, Good TestsGeeCON 2012 Bad Tests, Good Tests
GeeCON 2012 Bad Tests, Good TestsTomek Kaczanowski
 
Confitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good TestsConfitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good TestsTomek Kaczanowski
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockRobot Media
 
How to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeHow to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeDaniel Wellman
 
Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4jeresig
 
Testing in android
Testing in androidTesting in android
Testing in androidjtrindade
 
Navigating the xDD Alphabet Soup
Navigating the xDD Alphabet SoupNavigating the xDD Alphabet Soup
Navigating the xDD Alphabet SoupDror Helper
 
Ensure code quality with vs2012
Ensure code quality with vs2012Ensure code quality with vs2012
Ensure code quality with vs2012Sandeep Joshi
 
How to write clean tests
How to write clean testsHow to write clean tests
How to write clean testsDanylenko Max
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdfHans Jones
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotationjavatwo2011
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Librariesjeresig
 
NetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience ReportNetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience ReportAnton Arhipov
 
Android testing
Android testingAndroid testing
Android testingSean Tsai
 
Developer Test - Things to Know
Developer Test - Things to KnowDeveloper Test - Things to Know
Developer Test - Things to Knowvilniusjug
 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unitliminescence
 

Similar to 33rd Degree 2013, Bad Tests, Good Tests (20)

2013 DevFest Vienna - Bad Tests, Good Tests
2013 DevFest Vienna - Bad Tests, Good Tests2013 DevFest Vienna - Bad Tests, Good Tests
2013 DevFest Vienna - Bad Tests, Good Tests
 
GeeCON 2012 Bad Tests, Good Tests
GeeCON 2012 Bad Tests, Good TestsGeeCON 2012 Bad Tests, Good Tests
GeeCON 2012 Bad Tests, Good Tests
 
Confitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good TestsConfitura 2012 Bad Tests, Good Tests
Confitura 2012 Bad Tests, Good Tests
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
 
How to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeHow to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy Code
 
3 j unit
3 j unit3 j unit
3 j unit
 
Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4Testing, Performance Analysis, and jQuery 1.4
Testing, Performance Analysis, and jQuery 1.4
 
Testing in android
Testing in androidTesting in android
Testing in android
 
Navigating the xDD Alphabet Soup
Navigating the xDD Alphabet SoupNavigating the xDD Alphabet Soup
Navigating the xDD Alphabet Soup
 
Ensure code quality with vs2012
Ensure code quality with vs2012Ensure code quality with vs2012
Ensure code quality with vs2012
 
Junit and testNG
Junit and testNGJunit and testNG
Junit and testNG
 
How to write clean tests
How to write clean testsHow to write clean tests
How to write clean tests
 
Anti patterns
Anti patternsAnti patterns
Anti patterns
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdf
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Libraries
 
NetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience ReportNetBeans Plugin Development: JRebel Experience Report
NetBeans Plugin Development: JRebel Experience Report
 
Android testing
Android testingAndroid testing
Android testing
 
Developer Test - Things to Know
Developer Test - Things to KnowDeveloper Test - Things to Know
Developer Test - Things to Know
 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unit
 

More from Tomek Kaczanowski

Grupowe podejmowanie decyzji
Grupowe podejmowanie decyzjiGrupowe podejmowanie decyzji
Grupowe podejmowanie decyzjiTomek Kaczanowski
 
Sample Chapter of Practical Unit Testing with TestNG and Mockito
Sample Chapter of Practical Unit Testing with TestNG and MockitoSample Chapter of Practical Unit Testing with TestNG and Mockito
Sample Chapter of Practical Unit Testing with TestNG and MockitoTomek Kaczanowski
 
Practical Unit Testing with TestNG and Mockito
Practical Unit Testing with TestNG and MockitoPractical Unit Testing with TestNG and Mockito
Practical Unit Testing with TestNG and MockitoTomek Kaczanowski
 
GeeCON 2011 Who Watches The Watchmen? - On Quality Of Tests
GeeCON 2011 Who Watches The Watchmen? - On Quality Of TestsGeeCON 2011 Who Watches The Watchmen? - On Quality Of Tests
GeeCON 2011 Who Watches The Watchmen? - On Quality Of TestsTomek Kaczanowski
 
Convention Over Configuration - Maven 3, Polyglot Maven, Gradle and Ant
Convention Over Configuration - Maven 3, Polyglot Maven, Gradle and AntConvention Over Configuration - Maven 3, Polyglot Maven, Gradle and Ant
Convention Over Configuration - Maven 3, Polyglot Maven, Gradle and AntTomek Kaczanowski
 
Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010Tomek Kaczanowski
 

More from Tomek Kaczanowski (7)

2015 ACE! Conference slides
2015 ACE! Conference slides2015 ACE! Conference slides
2015 ACE! Conference slides
 
Grupowe podejmowanie decyzji
Grupowe podejmowanie decyzjiGrupowe podejmowanie decyzji
Grupowe podejmowanie decyzji
 
Sample Chapter of Practical Unit Testing with TestNG and Mockito
Sample Chapter of Practical Unit Testing with TestNG and MockitoSample Chapter of Practical Unit Testing with TestNG and Mockito
Sample Chapter of Practical Unit Testing with TestNG and Mockito
 
Practical Unit Testing with TestNG and Mockito
Practical Unit Testing with TestNG and MockitoPractical Unit Testing with TestNG and Mockito
Practical Unit Testing with TestNG and Mockito
 
GeeCON 2011 Who Watches The Watchmen? - On Quality Of Tests
GeeCON 2011 Who Watches The Watchmen? - On Quality Of TestsGeeCON 2011 Who Watches The Watchmen? - On Quality Of Tests
GeeCON 2011 Who Watches The Watchmen? - On Quality Of Tests
 
Convention Over Configuration - Maven 3, Polyglot Maven, Gradle and Ant
Convention Over Configuration - Maven 3, Polyglot Maven, Gradle and AntConvention Over Configuration - Maven 3, Polyglot Maven, Gradle and Ant
Convention Over Configuration - Maven 3, Polyglot Maven, Gradle and Ant
 
Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010
 

Recently uploaded

08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsRoshan Dwivedi
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 

Recently uploaded (20)

08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 

33rd Degree 2013, Bad Tests, Good Tests

  • 1. Bad Tests, Good Tests Tomek Kaczanowski http://twitter.com/#!/devops_borat
  • 2. Tomek Kaczanowski • Developer • Team lead • Blogger • http://kaczanowscy.pl/tomek • Book author • http://practicalunittesting.com • Working at CodeWise (Krakow, Poland) • ...we are hiring, wanna join us? JUnit version coming soon!
  • 3. Why bother with tests? • System works as expected • Changes do not hurt • Documentation
  • 4. Tests help to achieve quality Not sure when I saw this picture – probably in GOOS?
  • 5. What happens if we do it wrong? • Angry clients • Depressed developers http://www.joshcanhelp.com
  • 6. When I started out with unit tests, I was enthralled with the promise of ease and security that they would bring to my projects. In practice, however, the theory of sustainable software through unit tests started to break down. This difficulty continued to build up, until I finally threw my head back in anger and declared that "Unit Tests have become more trouble than they are worth." Llewellyn Falco and Michael Kennedy, Develop Mentor August 09
  • 9. write the right test write this test right
  • 10. Before we begin • All of the examples are real but were: • obfuscated • to protect the innocents :) • truncated • imagine much more complex domain objects • Asking questions is allowed • ...but being smarter than me is not ;)
  • 11. We don't need no stinkin' asserts! public void testAddChunks() { System.out.println("*************************************"); System.out.println("testAddChunks() ... "); ChunkMap cm = new ChunkMap(3); cm.addChunk(new Chunk("chunk")); List testList = cm.getChunks("chunk",null); if (testList.isEmpty()) fail("there should be at least one list!"); Chunk chunk = cm.getActualChunk("chunk",null); if (chunk.getElements().isEmpty()) fail("there should be at least one element!"); if (cm.getFinalChunkNr() != 1) fail("there should be at least one chunk!"); // iterate actual chunk for (Iterator it = chunk.getElements().iterator(); it.hasNext();) { Element element = (Element) it.next(); System.out.println("Element: " + element); } showChunks(cm); System.out.println("testAddChunks() OK "); } Courtesy of @bocytko
  • 12. Success is not an option... /** * Method testFailure. */ public void testFailure() { try { Message message = new Message(null,true); fail(); } catch(Exception ex) { ExceptionHandler.log(ExceptionLevel.ANY,ex); fail(); } } Courtesy of @bocytko
  • 13. What has happened? Well, it failed... public void testSimple() { IData data = null; IFormat format = null; LinkedList<String> attr = new LinkedList<String>(); attr.add("A"); attr.add("B"); try { format = new SimpleFormat("A"); data.setAmount(Amount.TEN); data.setAttributes(attr); IResult result = format.execute(); System.out.println(result.size()); Iterator iter = result.iterator(); while (iter.hasNext()) { IResult r = (IResult) iter.next(); System.out.println(r.getMessage()); ... } catch (Exception e) { fail(); } } Courtesy of @bocytko
  • 14. What has happened? Well, it failed... public void testSimple() { IData data = null; IFormat format = null; LinkedList<String> attr = new LinkedList<String>(); attr.add("A"); attr.add("B"); data is null - ready or not, NPE is coming!  try { format = new SimpleFormat("A"); data.setAmount(Amount.TEN); data.setAttributes(attr); IResult result = format.execute(); System.out.println(result.size()); Iterator iter = result.iterator(); while (iter.hasNext()) { IResult r = (IResult) iter.next(); System.out.println(r.getMessage()); ... } catch (Exception e) { fail(); } } Courtesy of @bocytko
  • 15. No smoke without tests class SystemAdminSmokeTest extends GroovyTestCase { void testSmoke() { def ds = new org.h2.jdbcx.JdbcDataSource( URL: 'jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=Oracle', user: 'sa', password: '') def jpaProperties = new Properties() jpaProperties.setProperty( 'hibernate.cache.use_second_level_cache', 'false') jpaProperties.setProperty( 'hibernate.cache.use_query_cache', 'false') def emf = new LocalContainerEntityManagerFactoryBean( dataSource: ds, persistenceUnitName: 'my-domain', jpaVendorAdapter: new HibernateJpaVendorAdapter( database: Database.H2, showSql: true, generateDdl: true), jpaProperties: jpaProperties) …some more code below }
  • 16. No smoke without tests class SystemAdminSmokeTest extends GroovyTestCase { void testSmoke() { // do not remove below code // def ds = new org.h2.jdbcx.JdbcDataSource( // URL: 'jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=Oracle', // user: 'sa', password: '') // // def jpaProperties = new Properties() // jpaProperties.setProperty( // 'hibernate.cache.use_second_level_cache', 'false') // jpaProperties.setProperty( // 'hibernate.cache.use_query_cache', 'false') // // def emf = new LocalContainerEntityManagerFactoryBean( // dataSource: ds, persistenceUnitName: 'my-domain', // jpaVendorAdapter: new HibernateJpaVendorAdapter( // database: Database.H2, showSql: true, // generateDdl: true), jpaProperties: jpaProperties) …some more code below, all commented out :( }
  • 17. Let's follow the leader! @Test public class ExampleTest { public void testExample() { assertTrue(true); } }
  • 18. Uh-oh, I feel lonely... @Test public class ExampleTest { public void testExample() { assertTrue(true); } }
  • 19. Conclusions • Automation! • Running • Verification • Do not live with broken window • And remember there is no one else to fix them but you! • It is a full time job! • You should be informed why your test failed • Master your tools • …at least learn the basics!
  • 20. Use of the real objects obscures the test @Test public void shouldGetTrafficTrend() { //given TrafficTrendProvider trafficTrendProvider = mock(TrafficTrendProvider.class); Report report = new Report(null, "", 1, 2, 3, BigDecimal.ONE, BigDecimal.ONE, 1); TrafficTrend trafficTrend = new TrafficTrend(report, report, new Date(), new Date(), new Date(), new Date()); given(trafficTrendProvider.getTrafficTrend()) .willReturn(trafficTrend); TrafficService service = new TrafficService(trafficTrendProvider); //when TrafficTrend result = service.getTrafficTrend(); //then assertThat(result).isEqualTo(trafficTrend); }
  • 21. Use of the real objects obscures the test @Test public void shouldGetTrafficTrend() { //given TrafficTrendProvider trafficTrendProvider = mock(TrafficTrendProvider.class); TrafficTrend trafficTrend = mock(TrafficTrend.class); given(trafficTrendProvider.getTrafficTrend()) .willReturn(trafficTrend); TrafficService service = new TrafficService(trafficTrendProvider); //when TrafficTrend result = service.getTrafficTrend(); //then assertThat(result).isEqualTo(trafficTrend); }
  • 22. Mock'em All! @Test public void shouldAddTimeZoneToModelAndView() { //given UserFacade userFacade = mock(UserFacade.class); ModelAndView modelAndView = mock(ModelAndView.class); given(userFacade.getTimezone()).willReturn("timezone X"); //when new UserDataInterceptor(userFacade) .postHandle(null, null, null, modelAndView); //then verify(modelAndView).addObject("timezone", "timezone X"); }
  • 23. ModelAndView from Mock'em All! SpringMVC – a mere container for data, without @Test any behaviour public void shouldAddTimeZoneToModelAndView() { //given UserFacade userFacade = mock(UserFacade.class); ModelAndView modelAndView = mock(ModelAndView.class); given(userFacade.getTimezone()).willReturn("timezone X"); //when new UserDataInterceptor(userFacade) .postHandle(null, null, null, modelAndView); //then verify(modelAndView).addObject("timezone", "timezone X"); }
  • 24. Use the front door @Test public void shouldAddTimeZoneToModelAndView() { //given UserFacade userFacade = mock(UserFacade.class); ModelAndView modelAndView = new ModelAndView(); given(userFacade.getTimezone()).willReturn("timezone X"); //when new UserDataInterceptor(userFacade) .postHandle(null, null, null, modelAndView); a pseudocode but that is what we mean //then assertThat(modelAndView).contains("timezone", "timezone X"); }
  • 25. Mock'em All! Public class Util { public String getUrl(User user, String timestamp) { String name = user.getFullName(); String url = baseUrl +"name="+URLEncoder.encode(name, "UTF-8") +"&timestamp="+timestamp; Developer wants to check return url; } whether timestamp is added to the URL when this method public String getUrl(User user) { is used Date date = new Date(); Long time = date.getTime()/1000; //convert ms to seconds String timestamp = time.toString(); return getUrl(user, timestamp); } }
  • 26. Mock'em All! Public class Util { public String getUrl(User user, String timestamp) { ... } public String getUrl(User user) { ... } } @Test public void shouldUseTimestampMethod() { //given Util util = new Util(); Util spyUtil = spy(util); //when spyUtil.getUrl(user); //then verify(spyUtil).getUrl(eq(user), anyString()); }
  • 27. Dependency injection Use the front door will save us @Test public void shouldAddTimestampToGeneratedUrl() { //given TimeProvider timeProvider = mock(TimeProvider.class); Util util = new Util(timeProvider); when(timeProvider.getTime()).thenReturn("12345"); util.set(timeProvider); //when String url = util.getUrl(user); //then assertThat(url).contains("timestamp=12345"); }
  • 28. Single Responsibility Principle A test should have one and only one reason to fail.
  • 29. Testing two things at once @DataProvider public Object[][] data() { return new Object[][] { {"48", true}, {"+48", true}, {"++48", true}, {"+48503", true}, {"+4", false}, {"++4", false}, {"", false}, {null, false}, {" ", false}, }; } @Test(dataProvider = "data") public void testQueryVerification(String query, boolean expected) { assertEquals(expected, FieldVerifier.isValidQuery(query)); }
  • 30. Testing two things at once @DataProvider public Object[][] data() { return new Object[][] { {"48", true}, {"+48", true}, {"++48", true}, {"+48503", true}, {"+4", false}, {"++4", false}, {"", false}, {null, false}, {" ", false}, }; } @Test(dataProvider = "data") public void testQueryVerification(String query, boolean expected) { assertEquals(expected, FieldVerifier.isValidQuery(query)); } testQueryVerification1() { assertEquals(true, FieldVerifier.isValidQuery(„48”)); } testQueryVerification2() { assertEquals(true, FieldVerifier.isValidQuery(„+48”)); } testQueryVerification3() { assertEquals(true, FieldVerifier.isValidQuery(„++48”)); } testQueryVerification4() { assertEquals(true, FieldVerifier.isValidQuery(„+48503”)); } ...
  • 31. Concentrate on one feature @DataProvider public Object[][] validQueries() { return new Object[][] { {"48"}, {"48123"}, {"+48"}, {"++48"}, {"+48503"}}; } @Test(dataProvider = "validQueries") public void shouldRecognizeValidQueries(String validQuery) { assertTrue(FieldVerifier.isValidQuery(validQuery)); } @DataProvider public Object[][] invalidQueries() { return new Object[][] { {"+4"}, {"++4"}, {""}, {null}, {" "} }; } @Test(dataProvider = "invalidQueries") public void shouldRejectInvalidQueries(String invalidQuery) { assertFalse(FieldVerifier.isValidQuery(invalidQuery)); }
  • 32. “And” @Test public void shouldReturnRedirectViewAndSendEmail() { //given given(bindingResult.hasErrors()).willReturn(false); given(userData.toEntity()).willReturn(user); given(userService.saveNewUser(eq(userData.toEntity()))) .willReturn(user); //when ModelAndView userRegisterResult = userRegisterController .registerUser(userData, bindingResult, request); //then assertThat(userRegisterResult.getViewName()) .isEqualTo("redirect:/signin"); verify(mailSender).sendRegistrationInfo(user); }
  • 33. One feature at a time @Test public void shouldRedirectToSigninPageWhenRegistrationSuceeded () { ... } @Test public void shouldNotifyAboutNewUserRegistration() { ... } Hint: forget about methods
  • 35. Who the heck is “user_2” ? @DataProvider public static Object[][] usersPermissions() { return new Object[][]{ {"user_1", Permission.READ}, {"user_1", Permission.WRITE}, {"user_1", Permission.REMOVE}, {"user_2", Permission.WRITE}, {"user_2", Permission.READ}, {"user_3", Permission.READ} }; }
  • 36. Ah, logged user can read and write... @DataProvider public static Object[][] usersPermissions() { return new Object[][]{ {ADMIN, Permission.READ}, {ADMIN, Permission.WRITE}, {ADMIN, Permission.REMOVE}, {LOGGED, Permission.WRITE}, {LOGGED, Permission.READ}, {GUEST, Permission.READ} }; }
  • 40. Do not make me learn the API! server = new MockServer(responseMap, true, new URL(SERVER_ROOT).getPort(), false);
  • 41. Do not make me learn the API! server = new MockServer(responseMap, true, new URL(SERVER_ROOT).getPort(), false); private static final boolean RESPONSE_IS_A_FILE = true; private static final boolean NO_SSL = false; server = new MockServer(responseMap, RESPONSE_IS_A_FILE, new URL(SERVER_ROOT).getPort(), NO_SSL);
  • 42. Do not make me learn the API! server = new MockServer(responseMap, true, new URL(SERVER_ROOT).getPort(), false); server = createFileNonSSLMockServer(responseMap);
  • 43. Do not make me learn the API! server = new MockServer(responseMap, true, new URL(SERVER_ROOT).getPort(), false); server = new MockServerBuilder() .withResponse(responseMap) .withResponseType(FILE) .withUrl(SERVER_ROOT) .withoutSsl().create(); server = MockServerBuilder .createFileNoSSLServer(responseMap, SERVER_ROOT);
  • 44. Naming is really important
  • 45. Test methods names are important • When test fails • Relation to focused tests
  • 46. Test methods names are important @Test public void testOperation() { configureRequest("/validate") rc = new RequestContext(parser, request) assert rc.getConnector() == null assert rc.getOperation().equals("validate") }
  • 47. “should” is better than “test” • testOperation() • testQuery() • testConstructor() • testFindUsersWithFilter() • shouldRejectInvalidRequests() • shouldSaveNewUserToDatabase() • constructorShouldFailWithNegativePrice() • shouldReturnOnlyUsersWithGivenName()
  • 48. “should” is better than “test” • Starting test method names with “should” steers you in the right direction. http://jochopra.blogspot.com/ • “test” prefix makes your test method a limitless bag where you throw everything worth testing http://www.greenerideal.com/
  • 49. Test methods names are important @Test public void testQuery(){ when(q.getResultList()).thenReturn(null); assertNull(dao.findByQuery(Transaction.class, q, false)); assertNull(dao.findByQuery(Operator.class, q, false)); assertNull(dao.findByQuery(null, null, false)); List result = new LinkedList(); when(q.getResultList()).thenReturn(result); assertEquals(dao.findByQuery(Transaction.class, q, false), result); assertEquals(dao.findByQuery(Operator.class, q, false), result); assertEquals(dao.findByQuery(null, null, false), null); when(q.getSingleResult()).thenReturn(null); assertEquals(dao.findByQuery(Transaction.class, q, true).size(), 0); assertEquals(dao.findByQuery(Operator.class, q, true).size(), 0); assertEquals(dao.findByQuery(null, null, true), null); when(q.getSingleResult()).thenReturn(t); assertSame(dao.findByQuery(Transaction.class, q, true).get(0), t); when(q.getSingleResult()).thenReturn(o); assertSame(dao.findByQuery(Operator.class, q, true).get(0), o); when(q.getSingleResult()).thenReturn(null); assertSame(dao.findByQuery(null, null, true), null); }
  • 50. Test methods names are important @Test public void shouldReturnNullListWhenDaoReturnsNull { when(q.getResultList()).thenReturn(null); assertNull(dao.findByQuery(Transaction.class, q, false)); assertNull(dao.findByQuery(Operator.class, q, false)); assertNull(dao.findByQuery(null, null, false)); } public void shouldReturnEmptyListWhenDaoReturnsIt { List result = new LinkedList(); when(q.getResultList()).thenReturn(result); assertEquals(dao.findByQuery(Transaction.class, q, false), result); assertEquals(dao.findByQuery(Operator.class, q, false), result); assertEquals(dao.findByQuery(null, null, false), null); } public void shouldReturnNullSingleResultWhenDaoReturnsNull { when(q.getSingleResult()).thenReturn(null); assertEquals(dao.findByQuery(Transaction.class, q, true).size(), 0); assertEquals(dao.findByQuery(Operator.class, q, true).size(), 0); assertEquals(dao.findByQuery(null, null, true), null); } public void shouldReturnSingleResultReturnedByDao { when(q.getSingleResult()).thenReturn(t); assertSame(dao.findByQuery(Transaction.class, q, true).get(0), t); when(q.getSingleResult()).thenReturn(o); assertSame(dao.findByQuery(Operator.class, q, true).get(0), o); when(q.getSingleResult()).thenReturn(null); assertSame(dao.findByQuery(null, null, true), null); }
  • 51. Assertion part is freaking huge public void shouldPreDeployApplication() { // given Artifact artifact = mock(Artifact.class); when(artifact.getFileName()).thenReturn("war-artifact-2.0.war"); ServerConfiguration config = new ServerConfiguration(ADDRESS, USER, KEY_FILE, TOMCAT_PATH, TEMP_PATH); Tomcat tomcat = new Tomcat(HTTP_TOMCAT_URL, config); String destDir = new File(".").getCanonicalPath() + SLASH + "target" + SLASH; new File(destDir).mkdirs(); // when tomcat.preDeploy(artifact, new FakeWar(WAR_FILE_LENGTH)); //then JSch jsch = new JSch(); jsch.addIdentity(KEY_FILE); Session session = jsch.getSession(USER, ADDRESS, 22); session.setConfig("StrictHostKeyChecking", "no"); session.connect(); Channel channel = session.openChannel("sftp"); session.setServerAliveInterval(92000); channel.connect(); ChannelSftp sftpChannel = (ChannelSftp) channel; sftpChannel.get(TEMP_PATH + SLASH + artifact.getFileName(), destDir); sftpChannel.exit(); session.disconnect(); File downloadedFile = new File(destDir, artifact.getFileName()); assertThat(downloadedFile).exists().hasSize(WAR_FILE_LENGTH); }
  • 52. Just say it public void shouldPreDeployApplication() { // given Artifact artifact = mock(Artifact.class); when(artifact.getFileName()) .thenReturn(ARTIFACT_FILE_NAME); ServerConfiguration config = new ServerConfiguration(ADDRESS, USER, KEY_FILE, TOMCAT_PATH, TEMP_PATH); Tomcat tomcat = new Tomcat(HTTP_TOMCAT_URL, config); // when tomcat.preDeploy(artifact, new FakeWar(WAR_FILE_LENGTH)); // then SSHServerAssert.assertThat(ARTIFACT_FILE_NAME) .existsOnServer(tomcat).hasSize(WAR_FILE_LENGTH); }
  • 53. Just say it public void shouldPreDeployApplication() { // given Artifact artifact = mock(Artifact.class); when(artifact.getFileName()) .thenReturn(ARTIFACT_FILE_NAME); ServerConfiguration config = new ServerConfiguration(ADDRESS, USER, KEY_FILE, TOMCAT_PATH, TEMP_PATH); Tomcat tomcat = new Tomcat(HTTP_TOMCAT_URL, config); // when WHY NOT CREATE WHY NOT USE tomcat.preDeploy(artifact, new FakeWar(WAR_FILE_LENGTH)); A PRIVATE ASSERTION METHOD? // then assertThatFileIsOnServer(ARTIFACT_FILE_NAME, Tomcat, WAR_FILE_LENGTH); }
  • 54. Asserting using private methods @Test public void testChargeInRetryingState() throws Exception { // given TxDTO request = createTxDTO(RequestType.CHARGE); AndroidTransaction androidTransaction = ... request.setTransaction(androidTransaction); // when final TxDTO txDTO = processor.processRequest(request); // then List<AndroidTransactionStep> steps = new ArrayList<>(androidTransaction.getSteps()); AndroidTransactionStep lastStep = steps.get(steps.size() - 1); assertEquals(lastStep.getTransactionState(), CHARGED_PENDING); assertEquals(txDTO.getResultCode(), CHARGED); }
  • 55. Asserting using private methods @Test public void testChargeInRetryingState() throws Exception { // given TxDTO request = createTxDTO(RequestType.CHARGE); AndroidTransaction androidTransaction = ... request.setTransaction(androidTransaction); // when WHY NOT CREATE final TxDTO txDTO = processor.processRequest(request); A PRIVATE ASSERTION METHOD? // then List<AndroidTransactionStep> steps = new ArrayList<>(androidTransaction.getSteps()); AndroidTransactionStep lastStep = steps.get(steps.size() - 1); assertEquals(lastStep.getTransactionState(), CHARGED_PENDING); assertEquals(txDTO.getResultCode(), CHARGED); }
  • 56. Asserting using private methods @Test public void testChargeInRetryingState() throws Exception { // given TxDTO request = createTxDTO(RequestType.CHARGE); AndroidTransaction androidTransaction = ... // when final TxDTO txDTO = processor.processRequest(request); // then assertState(request, androidTransaction, CHARGED, CHARGE_PENDING, AS_ANDROID_TX_STATE, ClientMessage.SUCCESS, ResultCode.SUCCESS); }
  • 57. Matchers vs. private methods assertState(TxDTO txDTO, AndroidTransaction androidTransaction, AndroidTransactionState expectedAndroidState, AndroidTransactionState expectedPreviousAndroidState, ExtendedState expectedState, String expectedClientStatus, ResultCode expectedRequestResultCode) { final List<AndroidTransactionStep> steps = new ArrayList<>(androidTransaction.getTransactionSteps()); final boolean checkPreviousStep = expectedAndroidState != null; assertTrue(steps.size() >= (checkPreviousStep ? 3 : 2)); if (checkPreviousStep) { AndroidTransactionStep lastStep = steps.get(steps.size() - 2); assertEquals(lastStep.getTransactionState(), expectedPreviousAndroidState); } final AndroidTransactionStep lastStep = steps.get(steps.size() - 1); assertEquals(lastStep.getTransactionState(), expectedAndroidState); assertEquals(lastStep.getMessage(), expectedClientStatus); assertEquals(txDTO.getResultCode(), expectedRequestResultCode); assertEquals(androidTransaction.getState(), expectedAndroidState); assertEquals(androidTransaction.getExtendedState(), expectedState); if (expectedClientStatus == null) { verifyZeroInteractions(client); } }
  • 58. Matchers vs. private methods @Test public void testChargeInRetryingState() throws Exception { // given TxDTO request = createTxDTO(CHARGE); AndroidTransaction androidTransaction = ... // when final TxDTO txDTO = processor.processRequest(request); // then assertThat(androidTransaction).hasState(CHARGED) .hasMessage(ClientMessage.SUCCESS) .hasPreviousState(CHARGE_PENDING) .hasExtendedState(null); assertEquals(txDTO.getResultCode(), ResultCode.SUCCESS); }
  • 59. Asserting implementation details public void invalidTxShouldBeCanceled() { ... String fileContent = FileUtils.getContentOfFile("response.csv"); assertTrue(fileContent.contains( "CANCEL,123,123cancel,billing_id_123_cancel,SUCCESS,")); }
  • 60. Asserting implementation details public void invalidTxShouldBeCanceled() { ... String fileContent = FileUtils.getContentOfFile("response.csv"); assertTrue(fileContent.contains( "CANCEL,123,123cancel,billing_id_123_cancel,SUCCESS,")); } public void invalidTxShouldBeCanceled() { ... String fileContent = FileUtils.getContentOfFile("response.csv"); TxDTOAssert.assertThat(fileContent) .hasTransaction("123cancel").withResultCode(SUCCESS); }
  • 61. Know your tools • Unit testing framework • Additional libraries • Use of temporary file rule • Hamcrest, FEST, Mockito, • Listeners catch-exception, awaitility, JunitParams, tempus-fugit, … • Concurrency • @Before/@After • Build tool • Parallel execution • Parametrized tests • CI • Test dependencies • IDE • Templates • Shortcuts
  • 62. Expected exceptions @Test(expected=IndexOutOfBoundsException.class) public void shouldThrowExceptionGettingElementOutsideTheList() { MyList<Integer> list = new MyList<Integer>(); list.add(0); list.add(1); list.get(2); }
  • 63. Expected exceptions @Test(expected=IndexOutOfBoundsException.class) public void shouldThrowExceptionGettingElementOutsideTheList() { MyList<Integer> list = new MyList<Integer>(); list.add(0); list.add(1); list.get(2); } http://code.google.com/p/catch-exception/ @Test public void shouldThrowExceptionGettingtElementOutsideTheList() { MyList<Integer> list = new MyList<Integer>(); list.add(0); list.add(1); catchException(list).get(2); assertThat(caughtException()) .isExactlyInstanceOf(IndexOutOfBoundsException.class); }
  • 64. Expected exceptions (with catch-exception) @Test public void shouldThrowException() throws SmsException { catchException(gutExtractor) .extractGut(„invalid gut”); then(caughtException()) .isInstanceOf(SmsException.class) .hasMessage("Invalid gut") .hasNoCause(); } http://code.google.com/p/catch-exception/
  • 65. Awaitility @Test public void updatesCustomerStatus() throws Exception { // Publish an asynchronous event: publishEvent(updateCustomerStatusEvent); // Awaitility lets you wait until // the asynchronous operation completes: await().atMost(5, SECONDS) .until(costumerStatusIsUpdated()); ... } http://code.google.com/p/awaitility/
  • 66. What do you really want to test? @Test public void shouldAddAUser() { User user = new User(); userService.save(user); assertEquals(dao.getNbOfUsers(), 1); }
  • 67. You wanted to see that the number increased @Test public void shouldAddAUser() { int nb = dao.getNbOfUsers(); User user = new User(); userService.save(user); assertEquals(dao.getNbOfUsers(), nb + 1); } Because: 1) This is closer to what you wanted to test 2) There is no assumption about the database “users” table being empty
  • 68. The dream of stronger, random-powered tests public void myTest() { SomeObject obj = new SomeObject( randomName(), randomValue(), ....); // testing of obj here } Does it make your test stronger?
  • 69. The dream of stronger, random-powered tests public void myTest() { SomeObject obj = new SomeObject( randomName(), randomValue(), ....); // testing of obj here } Does it make your test stronger? ...or does it only bring confusion? Test failed Expected SomeObject(„a”, „b”, ....) but got SomeObject(„*&O*$NdlF”, „#idSLNF”, ....)
  • 70. Random done wrong public void myTest() { SomeObject obj = new SomeObject( a, b, c, productCode()); // testing of obj here } private String productCode(){ String[] codes = {"Code A", "Code B", "Code C", "Code D"}; int index = rand.nextInt(codes.length); return codes[index]; }
  • 71. Ceremony @Test public void shouldBeAdministrator() { //given User user = new Administrator(); //when boolean administrator = user.isAdministrator() boolean advertiser = user.isAdvertiser(); boolean domainer = user.isDomainer(); //then assertThat(administrator).isTrue(); assertThat(advertiser).isFalse(); assertThat(domainer).isFalse(); }
  • 72. Ceremony @Test public void shouldBeAdministrator() { User user = new Administrator(); assertThat(user.isAdministrator()).isTrue(); assertThat(user.isAdvertiser()).isFalse(); assertThat(user.isDomainer()).isFalse(); }
  • 73. Asking for troubles... LoggingPropertyConfigurator configurator = mock(...); BaseServletContextListener baseServletContextListener = = new BaseServletContextListener(configurator) @Test public void shouldLoadConfigProperties() { Should load some baseServletContextListener.contextInitialized(); default config verify(configurator).configure(any(Properties.class)); } @Test(expected = LoggingInitialisationException.class) public void shouldThrowExceptionIfCantLoadConfiguration() Should load this { specific file System.setProperty("logConfig", "nonExistingFile"); baseServletContextListener.contextInitialized(); }
  • 74. Asking for troubles... LoggingPropertyConfigurator configurator = mock(...); BaseServletContextListener baseServletContextListener = = new BaseServletContextListener(configurator) @Test public void shouldLoadConfigProperties() { baseServletContextListener.contextInitialized(); verify(configurator).configure(any(Properties.class)); } @Test(expected = LoggingInitialisationException.class) public void shouldThrowExceptionIfCantLoadConfiguration() { System.setProperty("logConfig", "nonExistingFile"); baseServletContextListener.contextInitialized(); } @Before public void cleanSystemProperties() { ... }
  • 75. Test-last? No! • makes people not write tests at all • makes people do only happy path testing • tests reflect the implementation
  • 76. Always TDD? For six or eight hours spread over the next few weeks I struggled to get the first test written and running. Writing tests for Eclipse plug-ins is not trivial, so it’s not surprising I had some trouble. [...] In six or eight hours of solid programming time, I can still make significant progress. If I’d just written some stuff and verified it by hand, I would probably have the final answer to whether my idea is actually worth money by now. Instead, all I have is a complicated test that doesn’t work, a pile of frustration, eight fewer hours in my life, and the motivation to write another essay. Kent Beck, Just Ship, Baby
  • 77. There is so much more to discuss… • Integration / end-to-end tests which are not parametrized (so they all try to set up jetty on port 8080), • Tests which should be really unit, but use Spring context to create objects, • Tests with a lot of dependencies between them (a nightmare to maintain!), • Tests which are overspecified and will fail whenever you touch the production code, • Tests with monstrous objects-creation code, • Tests which run slow, • Tests which try to cover the deficiencies of production code and end up being a total mess, • Tests which verify methods instead of verifying responsibilities of a class, • Happy path tests, • etc., etc.
  • 78. Treat tests as the first class citizens • do it everyday or forget about it • make tests readable using matchers, • use the right tool for the job builders and good names • and learn to use it! • test behaviour not methods • do not live with broken windows • be pragmatic about the tests you write • respect KISS, SRP, DRY (?) • TDD always? • write good code, and you will also write • what is the best way to test it? good tests unit/integration/end-to-end ? • or rather write good tests and you • automate! will get good code for free • always concentrate on what is worth • code review your tests testing • do more than happy path testing • ask yourself questions like: 'is it really important that X should send • do not make the reader learn the API, message Y to Z?' make it obvious • use the front door – state testing before • bad names lead to bad tests interaction testing (mocks)
  • 79. Thank you! You can learn more about writing high quality tests by reading my book – „Practical Unit Testing”. You can also participate in writing of my new (free!) e- book devoted to bad and good tests.