Mocks, Stubs and Fakes.
¿What Else?.

alexsotob - lordofthejars.com

Continuous Integration

Commit Stage

Unit tests

FIRST

Fast, Isolated, Repeteable, Self-Validating, Timely

Test Double

Mocks

Mocks

The Good and The Bad

Dependencies

testCompile ('org.mockito:mockito-all:1.10.19') {
  exclude group: 'org.hamcrest'
}

Init

@RunWith(MockitoJUnitRunner.class)
public class Test {
  @Mock EmailService email;
}

public class Test {

  @Mock EmailService email;
  
  @Before public void before() {
    MockitoAnnotations.initMocks(this)
  }
}

EmailService emailService = Mockito.mock(EmailService.class);

Example

Mockito.when(email.receiveBodyMessagesWithSubject("My Subject")).thenReturn("This is My Message")

System.out.println(email.receiveBodyMessagesWithSubject("Wrong subject"));
System.out.println(email.receiveBodyMessagesWithSubject("My Subject"));
System.out.println(email.receiveBodyMessagesWithSubject("My Subject"));

Example With Any

Mockito.when(email.receiveBodyMessagesWithSubject(Matchers.anyString())).thenReturn("This is My Message");

System.out.println(email.receiveBodyMessagesWithSubject("Subject"));

Example Verify

InvoiceService invoiceService = new InvoiceService(email);
invoiceService.send();

Mockito.verify(email, Mockito.times(1)).sendMessage(Matchers.anyString(), Matchers.anyString());

Example Argument Captor

InvoiceService invoiceService = new InvoiceService(email);
invoiceService.send();

ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(email, Mockito.times(1)).sendMessage(argument.capture(), Matchers.anyString());

Assert.assertThat(argument.getValue(), CoreMatchers.startsWith("Invoice:"));

Example Spy

EmailService email = new EmailService();
EmailService spyEmail = Mockito.spy(email);
InvoiceService invoiceService = new InvoiceService(spyEmail);
invoiceService.send();

Mockito.verify(spyEmail, Mockito.times(1)).sendMessage(argument.capture(), Matchers.anyString());

Dependencies

testCompile ('org.powermock:powermock-module-junit4:1.6.4')
testCompile ('org.powermock:powermock-api-mockito:1.6.4')

Prepare for Mocking

@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticClass.class)
public class MyTest {
}

Mock Static

PowerMockito.mockStatic(StaticClass.class);
Mockito.when(StaticClass.getValue()).thenReturn("Hello");

System.out.println(StaticClass.getValue());
PowerMockito.verifyStatic();

Mock Constructor

PowerMockito.whenNew(MyClass.class)
              .withNoArguments()
              .thenThrow(new IOException("error message"));
              
X x = new X();
x.createMyClass();
PowerMockito.verifyNew(MyClass.class).withNoArguments();

Private Method

Calculator calculator = PowerMockito.spy(new Calculator());
PowerMockito.doReturn(true).when(calculator, "isCalculated");

System.out.println(calculator.calculate());
PowerMockito.verifyPrivate(calculator).invoke("isCalculated");

Be Aware

All that Glitters is not Gold

Example of System

if (System.currentTimeMillis() > pojo.getStartDate() + expiration) {
}

Java 8

java.time.Clock clock;
public void X() {
  if (eventDate.isBefore(LocalDate.now(clock)) {
  }
}

Java

TimeResolution clock;
public void X() {
  if (clock.now() > pojo.getStartDate() + expiration) {
  }
}

Stubs

Stubs

Email Service

public class EmailServiceStub implements EmailService {
  private List emails = new ArrayList<>();
  
  public void sendMessage(String subject, String body) {
    this.emails.add(new Email(subject, body));
  }
  
  public String receiveBodyMessagesWithSubject(String subject) {
    // ...
  }
}

The Good and The Bad

Wiremock

@Rule
public WireMockRule wireMockRule = new WireMockRule(8089);

stubFor(get(urlEqualTo("/planets/"))
    .willReturn(aResponse()
            .withHeader("Content-Type", "application/json")
            .withBody(Files.readAllBytes(configFile.toPath())))
);

client.target("http://localhost:8089/").path("planets/");

Fakes

Fakes

The Good and The Bad

Mocking 2.0

Byteman

testCompile ('org.jboss.byteman:byteman-bmunit:3.0.3') {
  exclude group: 'org.testng'
}

Byteman

public WebWriter(String filename) {
  this.filename = filename;
  
}

public PrintStream openFile() {
  File file = new File(filename);
  try {
    FileOutputStream fos = new FileOutputStream(file);
    PrintStream ps = new PrintStream(fos, true);
    return ps;
  } catch (FileNotFoundException e) {
    System.out.println("Unable to open file " + file.getName());
    System.out.println(e);
    return null;
  }
}

Byteman

@RunWith(org.jboss.byteman.contrib.bmunit.BMUnitRunner.class)
public class Test {

  @Test
  @BMRule(name = "handle file not found",
    targetClass = "java.io.FileOutputStream",
    targetMethod = "(File)",
    action = "throw new FileNotFoundException( \"Ha ha Byteman fooled you again!\" )"
  )
  public void handleFileNotFound() throws FileNotFoundException {
    System.out.println("-------- handleFileNotFound ---------");
    WebWriter writer = new WebWriter("foo.html", "Andrew");
    PrintStream ps = writer.openFile();
    Assert.assertTrue(ps == null);
    System.out.println("-------- handleFileNotFound ---------\n");
  }
}

Docker as Testing Tool

Continuous Integration As Code

Pipeline Plugin

Jenkinsfile (1/2)

stage 'compileAndUnit'
node {
        git branch: 'master', url: 'https://github.com/lordofthejars/starwars.git'
        gradle 'clean test'
        stash excludes: 'build/', includes: '**', name: 'source'
        stash includes: 'build/jacoco/*.exec', name: 'unitCodeCoverage'
        step([$class: 'JUnitResultArchiver', testResults: '**/build/test-results/*.xml'])
}

stage 'codeQuality'
parallel 'pmd' : {
    node {
        unstash 'source'
        gradle 'pmdMain'
        step([$class: 'PmdPublisher', pattern: 'build/reports/pmd/*.xml'])
    }
}, 'jacoco': {
    node {
        unstash 'source'
        unstash 'unitCodeCoverage'
        gradle 'jacocoTestReport'
    }
}

Jenkinsfile (2/2)

stage 'assemble-binaries'

node('linux') {
    unstash 'source'
    withEnv(["SOURCE_BUILD_NUMBER=${env.BUILD_NUMBER}"]) {
        gradle 'assemble'
    }
}

input message: "Deploy Application to QA ?"

stage name: 'Deploy to QA', concurrency: 1
node {
    echo "Application Deployed to QA."
}

Let's wind
down

What You have Seen

Questions


http://lordofthejars.github.io/mock-stub-fake/

asotobu@gmail.com

https://github.com/lordofthejars/starwars

http://site.mockito.org/

https://github.com/jayway/powermock

http://wiremock.org/

http://arquillian.org/

http://byteman.jboss.org/

https://github.com/jenkinsci/workflow-plugin