Multipurpose tests Cool tricks with JUnit Java Zone

  • Slides: 57
Download presentation
Multi-purpose tests (Cool tricks with JUnit) Java. Zone 2012 Johannes Brodwall, Principal Architect Steria

Multi-purpose tests (Cool tricks with JUnit) Java. Zone 2012 Johannes Brodwall, Principal Architect Steria Norway @jhannes

Part I

Part I

Properties of good test suites

Properties of good test suites

Fast feedback • Reasonable confidence in seconds

Fast feedback • Reasonable confidence in seconds

While you type! Holy crap! Get Infinitest! http: //infinitest. github. com/

While you type! Holy crap! Get Infinitest! http: //infinitest. github. com/

Good coverage • Never mind measured code coverage • Do you catch many potential

Good coverage • Never mind measured code coverage • Do you catch many potential mistakes?

Robust • • Does refactoring break you tests? Do tests interfere with each other?

Robust • • Does refactoring break you tests? Do tests interfere with each other?

While you type! Holy crap! “Act” and “assert” at same level of Get Infinitest!

While you type! Holy crap! “Act” and “assert” at same level of Get Infinitest! abstraction http: //infinitest. github. com/ @Test public void should_filter_people() { Person person = Person. with. Name("Darth Vader"); Person non. Matching = Person. with. Name("Anakin Skywalker"); person. Repository. save. Person(person); person. Repository. save. Person(non. Matching); assert. That(person. Repository. find. People("vade")). contains(person). excludes(non. Matching); } Allows for garbage in repository

Good test suites • • Fast feedback – ok confidence in seconds • Coverage

Good test suites • • Fast feedback – ok confidence in seconds • Coverage – breaks when they should Robust – don’t break when they shouldn’t

“Integration test” • Sacrifice feedback for coverage

“Integration test” • Sacrifice feedback for coverage

Part II

Part II

The configurable test

The configurable test

Starting point

Starting point

public class Person. Servlet. Test { private Person. Servlet servlet = new Person. Servlet();

public class Person. Servlet. Test { private Person. Servlet servlet = new Person. Servlet(); Http. Servlet. Request req = mock(Http. Servlet. Req. class); Http. Servlet. Response resp = mock(Http. Servlet. Resp. class); Person. Repository person. Repository = mock(Person. Rep. class); @Test public void should_save_person() throws IOException { when(req. get. Parameter("full_name")). then. Return("Darth Vader"); servlet. do. Post(req, resp); verify(person. Repository). save. Person(Person. with. Name("Darth Vader")); }

Fake instead of mock

Fake instead of mock

public class Person. Servlet. Test { private Person. Servlet servlet = new Person. Servlet();

public class Person. Servlet. Test { private Person. Servlet servlet = new Person. Servlet(); private Http. Servlet. Request req = mock(Http. Servlet. Req. class); private Http. Servlet. Response resp = mock(Http. Servlet. Resp. class); private Person. Repository person. Repository = new Fake. Person. Repository(); @Test public void should_save_person() throws IOException { when(req. get. Parameter("full_name")). then. Return("Darth Vader"); servlet. do. Post(req, resp); assert. That(person. Repository. find. All()). contains(Person. with. Name("Darth Vader")); } }

But we want also to use the real thing

But we want also to use the real thing

public class Person. Servlet With. Real. Repo. Test { private Person. Servlet servlet =

public class Person. Servlet With. Real. Repo. Test { private Person. Servlet servlet = new Person. Servlet(); private Http. Servlet. Request req = mock(Http. Servlet. Req. class); private Http. Servlet. Response resp = mock(Http. Servlet. Resp. class); private Person. Repository person. Repository = new Jdbc. Person. Repository(…); @Test public void should_save_person() throws IOException { when(req. get. Parameter("full_name")). then. Return("Darth Vader"); servlet. do. Post(req, resp); assert. That(person. Repository. find. All()). contains(Person. with. Name("Darth Vader")); } }

What if one test could do both?

What if one test could do both?

@Run. With(Repository. Test. Runner. class) public class Person. Servlet. Test { private Person. Servlet

@Run. With(Repository. Test. Runner. class) public class Person. Servlet. Test { private Person. Servlet servlet = new Person. Servlet(); Http. Servlet. Request req = mock(Http. Servlet. Req. class); Http. Servlet. Response resp = mock(Http. Servlet. Resp. class); Person. Repository person. Repository; public Person. Servlet. Test(Person. Repository person. Repo) { this. person. Repository = person. Repo; } @Test public void should_save_person() throws IOException { … } }

I learned to write my own test runner by looking at org. junit. runner.

I learned to write my own test runner by looking at org. junit. runner. Parameterized

The runner

The runner

public class Repository. Test. Runner extends Suite { private static Jdbc. Connection. Pool data.

public class Repository. Test. Runner extends Suite { private static Jdbc. Connection. Pool data. Source = …; public Repository. Test. Runner(Class<? > test. Class) { super(test. Class, create. Runners(test. Class)); } private static List<Runner> create. Runners(Class<? > test. Class) { List<Runner> runners = new Array. List<>(); runners. add(new Repo. Test. Runner(new Fake. Person. Repository(), test. Class)); runners. add(new Repo. Test. Runner(new Jdbc. Person. Repository(data. Source), test. Class)); return runners; } public static class Repo. Test. Runner extends Block. JUnit 4 Class. Runner {

public class Repository. Test. Runner extends Suite { public static class Repo. Test. Runner

public class Repository. Test. Runner extends Suite { public static class Repo. Test. Runner extends Block. JUnit 4 Class. Runner { private Person. Repository person. Repo; Repo. Test. Runner(Person. Repository person. Repo, Class<? > test. Class) { super(test. Class); this. person. Repo = person. Repo; } protected Object create. Test() throws Exception { return get. Test. Class(). get. Only. Constructor(). new. Instance(person. Repo); } protected void validate. Constructor(List<Throwable> errors) { validate. Only. One. Constructor(errors); } } }

The result

The result

Keeping it honest

Keeping it honest

@Run. With(Repository. Test. Runner. class) public class Person. Repository. Test { private Person. Repository

@Run. With(Repository. Test. Runner. class) public class Person. Repository. Test { private Person. Repository person. Repository; public Person. Repository. Test(Person. Repository person. Repository) { this. person. Repository = person. Repository; } @Test public void should_filter_people() { Person person = Person. with. Name("Darth Vader"); Person non. Matching = Person. with. Name("Anakin Skywalker"); person. Repository. save. Person(person); person. Repository. save. Person(non. Matching); assert. That(person. Repository. find. People("vade")). contains(person). excludes(non. Matching); } }

https: //github. com/jhannes/javaee-spike

https: //github. com/jhannes/javaee-spike

Part III

Part III

The big picture

The big picture

Worth it?

Worth it?

Thought experiment: What if you could test your whole application without connecting to anything?

Thought experiment: What if you could test your whole application without connecting to anything?

Maintain fakes or Speed up realz?

Maintain fakes or Speed up realz?

Maintain fakes or Speed up realz?

Maintain fakes or Speed up realz?

Other uses

Other uses

@Run. With(File. Test. Runner. class) @File. Test. Runner. Directory("src/test/xml/samples") public class Xml. Serialization. Test

@Run. With(File. Test. Runner. class) @File. Test. Runner. Directory("src/test/xml/samples") public class Xml. Serialization. Test { private final File xml. File; public Xml. Serialization. Test(File xml. File) { this. xml. File = xml. File; } @Test public void serialized. Should. Match() { assert. That(normalize(Xml. read(xml. File). to. XML())). is. Equal. To(normalize(slurp(xml. File))); }

https: //github. com/jhannes/eaxy

https: //github. com/jhannes/eaxy

Conclusions

Conclusions

There’s more to tests than “integration test” or “unit test”

There’s more to tests than “integration test” or “unit test”

 • Get fast feedback • Detect mistakes • Only fail when you should

• Get fast feedback • Detect mistakes • Only fail when you should

Thank you johannes@brodwall. com http: //johannesbrodwall. com http: //github. com/jhannes http: //twitter. com/jhannes

Thank you johannes@brodwall. com http: //johannesbrodwall. com http: //github. com/jhannes http: //twitter. com/jhannes

“Integration tests or unit tests”

“Integration tests or unit tests”

Integration tests or unit test?

Integration tests or unit test?

public class Jdbc. Person. Repository. Test { private static Data. Source data. Source; @Before.

public class Jdbc. Person. Repository. Test { private static Data. Source data. Source; @Before. Class public static void create. Data. Source() { data. Source = Jdbc. Connection. Pool. create(…); Jdbc. Person. Repository. create. Database. Schema(data. Source); } Person. Repository person. Repo = new Jdbc. Person. Repository(data. Source); @Test public void should_save_people() { Person person = Person. with. Name("Johannes Brodwall"); person. Repo. save. Person(person); assert. That(person. Repo. find. People(null)). contains(person); } }

“Integration tests” • Connects to services, database, files? • Or just integrates whole system?

“Integration tests” • Connects to services, database, files? • Or just integrates whole system? • Or integrates system with all services?

“Integration tests” are • Slow • Dependent on external systems • Realistic?

“Integration tests” are • Slow • Dependent on external systems • Realistic?

Integration tests or unit test?

Integration tests or unit test?

public class Person. Test { @Test public void equality() { assert. That(Person. with. Name("Darth

public class Person. Test { @Test public void equality() { assert. That(Person. with. Name("Darth Vader")). is. Equal. To(Person. with. Name("Darth Vader")). is. Not. Equal. To(Person. with. Name("Anakin Skywalker")). is. Not. Equal. To(new Object()). is. Not. Equal. To(null); } } Since you’re asking: FEST-assert http: //code. google. com/p/fest/

“Unit tests” • No resources outside JVM? • Only one class? • Only one

“Unit tests” • No resources outside JVM? • Only one class? • Only one method?

“Unit tests” are • Very fast • Coupled to implementation?

“Unit tests” are • Very fast • Coupled to implementation?

Integration tests or unit test?

Integration tests or unit test?

Why does it matter?

Why does it matter?

Who is this talk for? • • • You have written more than a

Who is this talk for? • • • You have written more than a few tests in JUnit You want to find out when you write unit test and when to write integration tests and how to use mocks well You will be able to write your own test-runner for fun and profit

 • What will I cover Testing considerations • • Creating your own test

• What will I cover Testing considerations • • Creating your own test runner • • • Unit test or integration test What are good tests Mocks versus fakes The mocked test • The faked test The multi purpose test • What’s next? • Eventually abandoned But still using @Run. With in many cases – eg. Files •