diff --git a/pom.xml b/pom.xml index 2105a513e20b8afac9b8b20a2939bb401262c2ec..a5243ec3f9bf9148745b717346fe30b1cbe8c6ed 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ 4.12 + 2.0.0.0 3.4 2.49.1 4.2.4.RELEASE @@ -45,7 +46,7 @@ 2.19.1 2.6 2.2 - 0.7.5.201505241946 + 0.7.6.201602180812 1.4.18 @@ -83,6 +84,12 @@ test + + org.hamcrest + java-hamcrest + ${java-hamcrest.version} + + org.easymock easymock diff --git a/src/test/java/es/uvigo/esei/daa/dao/PeopleDAOTest.java b/src/test/java/es/uvigo/esei/daa/dao/PeopleDAOTest.java index 3dd43477612584574e6afded3c36380cb14eb53d..68d93675fe9e7004aa2f7a29d50fa33d51da1a3e 100644 --- a/src/test/java/es/uvigo/esei/daa/dao/PeopleDAOTest.java +++ b/src/test/java/es/uvigo/esei/daa/dao/PeopleDAOTest.java @@ -1,6 +1,18 @@ package es.uvigo.esei.daa.dao; -import static org.junit.Assert.assertEquals; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentId; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newName; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newSurname; +import static es.uvigo.esei.daa.dataset.PeopleDataset.nonExistentId; +import static es.uvigo.esei.daa.dataset.PeopleDataset.nonExistentPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.people; +import static es.uvigo.esei.daa.dataset.PeopleDataset.peopleWithout; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.containsPeopleInAnyOrder; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.equalsToPerson; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; import javax.sql.DataSource; @@ -46,53 +58,53 @@ public class PeopleDAOTest { this.dao = new PeopleDAO(); } + @Test + public void testList() throws DAOException { + assertThat(this.dao.list(), containsPeopleInAnyOrder(people())); + } + @Test public void testGet() throws DAOException { - final Person person = this.dao.get(4); + final Person person = this.dao.get(existentId()); - assertEquals(4, person.getId()); - assertEquals("María", person.getName()); - assertEquals("Márquez", person.getSurname()); + assertThat(person, is(equalsToPerson(existentPerson()))); } @Test(expected = IllegalArgumentException.class) public void testGetNonExistentId() throws DAOException { - this.dao.get(100); - } - - @Test - public void testList() throws DAOException { - assertEquals(10, this.dao.list().size()); + this.dao.get(nonExistentId()); } @Test @ExpectedDatabase("/datasets/dataset-delete.xml") public void testDelete() throws DAOException { - this.dao.delete(4); - - assertEquals(9, this.dao.list().size()); + this.dao.delete(existentId()); + + assertThat(this.dao.list(), containsPeopleInAnyOrder(peopleWithout(existentId()))); } @Test(expected = IllegalArgumentException.class) public void testDeleteNonExistentId() throws DAOException { - this.dao.delete(100); + this.dao.delete(nonExistentId()); } @Test @ExpectedDatabase("/datasets/dataset-modify.xml") public void testModify() throws DAOException { - this.dao.modify(new Person(5, "John", "Doe")); + final Person person = existentPerson(); + person.setName(newName()); + person.setSurname(newSurname()); + + this.dao.modify(person); - final Person person = this.dao.get(5); + final Person persistentPerson = this.dao.get(person.getId()); - assertEquals(5, person.getId()); - assertEquals("John", person.getName()); - assertEquals("Doe", person.getSurname()); + assertThat(persistentPerson, is(equalsToPerson(person))); } @Test(expected = IllegalArgumentException.class) public void testModifyNonExistentId() throws DAOException { - this.dao.modify(new Person(100, "John", "Doe")); + this.dao.modify(nonExistentPerson()); } @Test(expected = IllegalArgumentException.class) @@ -103,25 +115,22 @@ public class PeopleDAOTest { @Test @ExpectedDatabase("/datasets/dataset-add.xml") public void testAdd() throws DAOException { - final Person person = this.dao.add("John", "Doe"); + final Person person = this.dao.add(newName(), newSurname()); - assertEquals("John", person.getName()); - assertEquals("Doe", person.getSurname()); + assertThat(person, is(equalsToPerson(newPerson()))); - final Person personGet = this.dao.get(person.getId()); + final Person persistentPerson = this.dao.get(person.getId()); - assertEquals(person.getId(), personGet.getId()); - assertEquals("John", personGet.getName()); - assertEquals("Doe", personGet.getSurname()); + assertThat(persistentPerson, is(equalsToPerson(newPerson()))); } @Test(expected = IllegalArgumentException.class) public void testAddNullName() throws DAOException { - this.dao.add(null, "Doe"); + this.dao.add(null, newSurname()); } @Test(expected = IllegalArgumentException.class) public void testAddNullSurname() throws DAOException { - this.dao.add("John", null); + this.dao.add(newName(), null); } } diff --git a/src/test/java/es/uvigo/esei/daa/dao/PeopleDAOUnitTest.java b/src/test/java/es/uvigo/esei/daa/dao/PeopleDAOUnitTest.java index b9716e450b16d72f464d71b9df436647d1e38ace..94ac74b92a0cf20ef651f4f7e4cdd7b908f0ee50 100644 --- a/src/test/java/es/uvigo/esei/daa/dao/PeopleDAOUnitTest.java +++ b/src/test/java/es/uvigo/esei/daa/dao/PeopleDAOUnitTest.java @@ -1,109 +1,106 @@ package es.uvigo.esei.daa.dao; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentId; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newName; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newSurname; +import static es.uvigo.esei.daa.dataset.PeopleDataset.people; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.containsPeopleInAnyOrder; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.equalsToPerson; import static org.easymock.EasyMock.anyString; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.reset; -import static org.junit.Assert.assertEquals; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; import org.junit.Test; -import es.uvigo.esei.daa.DatabaseQueryUnitTest; +import com.mysql.jdbc.Statement; + import es.uvigo.esei.daa.entities.Person; +import es.uvigo.esei.daa.util.DatabaseQueryUnitTest; public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { @Test - public void testGet() throws Exception { - final Person person = new Person(1, "Pepe", "Pérez"); + public void testList() throws Exception { + final Person[] people = people(); - expect(result.next()).andReturn(true); - expect(result.getInt("id")).andReturn(person.getId()); - expect(result.getString("name")).andReturn(person.getName()); - expect(result.getString("surname")).andReturn(person.getSurname()); + for (Person person : people) { + expectPersonRow(person); + } + expect(result.next()).andReturn(false); result.close(); replayAll(); - final PeopleDAO peopleDAO = new PeopleDAO(); - - assertEquals("Unexpected person data", - person, peopleDAO.get(person.getId()) - ); + + assertThat(peopleDAO.list(), containsPeopleInAnyOrder(people)); } - @Test(expected = IllegalArgumentException.class) - public void testGetMissing() throws Exception { - expect(result.next()).andReturn(false); + @Test(expected = DAOException.class) + public void testListUnexpectedException() throws Exception { + expect(result.next()).andThrow(new SQLException()); result.close(); replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.get(2); + peopleDAO.list(); } - @Test(expected = DAOException.class) - public void testGetUnexpectedException() throws Exception { - expect(result.next()).andThrow(new SQLException()); + @Test + public void testGet() throws Exception { + final Person existentPerson = existentPerson(); + + expectPersonRow(existentPerson); result.close(); replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.get(2); - } - - @Test - public void testList() throws Exception { - final List people = Arrays.asList( - new Person(1, "Pepe", "Pérez"), - new Person(2, "Paco", "Martínez"), - new Person(3, "Martina", "Juárez") - ); - for (Person person : people) { - expect(result.next()).andReturn(true); - expect(result.getInt("id")).andReturn(person.getId()); - expect(result.getString("name")).andReturn(person.getName()); - expect(result.getString("surname")).andReturn(person.getSurname()); - } + assertThat(peopleDAO.get(existentId()), is(equalTo(existentPerson))); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetMissing() throws Exception { expect(result.next()).andReturn(false); result.close(); replayAll(); + final PeopleDAO peopleDAO = new PeopleDAO(); - - assertEquals("Unexpected people data", - people, peopleDAO.list() - ); + peopleDAO.get(existentId()); } @Test(expected = DAOException.class) - public void testListUnexpectedException() throws Exception { + public void testGetUnexpectedException() throws Exception { expect(result.next()).andThrow(new SQLException()); result.close(); replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.list(); + peopleDAO.get(existentId()); } @Test public void testAdd() throws Exception { - final Person person = new Person(1, "Pepe", "Pérez"); - + final Person person = newPerson(); reset(connection); - expect(connection.prepareStatement(anyString(), eq(1))) + expect(connection.prepareStatement(anyString(), eq(Statement.RETURN_GENERATED_KEYS))) .andReturn(statement); expect(statement.executeUpdate()).andReturn(1); expect(statement.getGeneratedKeys()).andReturn(result); + + // Key retrieval expect(result.next()).andReturn(true); - expect(result.getInt(1)).andReturn(person.getId()); // Key retrieval + expect(result.getInt(1)).andReturn(person.getId()); connection.close(); result.close(); @@ -112,7 +109,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { final PeopleDAO peopleDAO = new PeopleDAO(); final Person newPerson = peopleDAO.add(person.getName(), person.getSurname()); - assertEquals(person, newPerson); + assertThat(newPerson, is(equalsToPerson(person))); } @Test(expected = IllegalArgumentException.class) @@ -123,7 +120,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { resetAll(); // No expectations - peopleDAO.add(null, "Pepe"); + peopleDAO.add(null, newSurname()); } @Test(expected = IllegalArgumentException.class) @@ -134,7 +131,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { resetAll(); // No expectations - peopleDAO.add("Pepe", null); + peopleDAO.add(newName(), null); } @Test(expected = DAOException.class) @@ -148,7 +145,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.add("Paco", "Pérez"); + peopleDAO.add(newName(), newSurname()); } @Test(expected = DAOException.class) @@ -165,7 +162,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.add("Paco", "Pérez"); + peopleDAO.add(newName(), newSurname()); } @Test(expected = DAOException.class) @@ -179,7 +176,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.add("Paco", "Pérez"); + peopleDAO.add(newName(), newSurname()); } @Test @@ -189,7 +186,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.delete(1); + peopleDAO.delete(existentId()); } @Test(expected = IllegalArgumentException.class) @@ -199,7 +196,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.delete(1); + peopleDAO.delete(existentId()); } @Test(expected = DAOException.class) @@ -209,19 +206,17 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.delete(1); + peopleDAO.delete(existentId()); } @Test public void testModify() throws Exception { - final Person person = new Person(1, "Pepe", "Pérez"); - expect(statement.executeUpdate()).andReturn(1); replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.modify(person); + peopleDAO.modify(existentPerson()); } @Test(expected = IllegalArgumentException.class) @@ -242,7 +237,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.modify(new Person(1, "Paco", "Pérez")); + peopleDAO.modify(existentPerson()); } @Test(expected = DAOException.class) @@ -252,6 +247,13 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { replayAll(); final PeopleDAO peopleDAO = new PeopleDAO(); - peopleDAO.modify(new Person(1, "Paco", "Pérez")); + peopleDAO.modify(existentPerson()); + } + + private void expectPersonRow(Person person) throws SQLException { + expect(result.next()).andReturn(true); + expect(result.getInt("id")).andReturn(person.getId()); + expect(result.getString("name")).andReturn(person.getName()); + expect(result.getString("surname")).andReturn(person.getSurname()); } } diff --git a/src/test/java/es/uvigo/esei/daa/dataset/PeopleDataset.java b/src/test/java/es/uvigo/esei/daa/dataset/PeopleDataset.java new file mode 100644 index 0000000000000000000000000000000000000000..764a44f32ca073bfff43697da7e915ab6c0b4d9b --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/dataset/PeopleDataset.java @@ -0,0 +1,74 @@ +package es.uvigo.esei.daa.dataset; + +import static java.util.Arrays.binarySearch; +import static java.util.Arrays.stream; + +import java.util.Arrays; +import java.util.function.Predicate; + +import es.uvigo.esei.daa.entities.Person; + +public final class PeopleDataset { + private PeopleDataset() {} + + public static Person[] people() { + return new Person[] { + new Person(1, "Antón", "Álvarez"), + new Person(2, "Ana", "Amargo"), + new Person(3, "Manuel", "Martínez"), + new Person(4, "María", "Márquez"), + new Person(5, "Lorenzo", "López"), + new Person(6, "Laura", "Laredo"), + new Person(7, "Perico", "Palotes"), + new Person(8, "Patricia", "Pérez"), + new Person(9, "Julia", "Justa"), + new Person(10, "Juan", "Jiménez") + }; + } + + public static Person[] peopleWithout(int ... ids) { + Arrays.sort(ids); + + final Predicate hasValidId = person -> + binarySearch(ids, person.getId()) < 0; + + return stream(people()) + .filter(hasValidId) + .toArray(Person[]::new); + } + + public static Person person(int id) { + return stream(people()) + .filter(person -> person.getId() == id) + .findAny() + .orElseThrow(IllegalArgumentException::new); + } + + public static int existentId() { + return 5; + } + + public static int nonExistentId() { + return 1234; + } + + public static Person existentPerson() { + return person(existentId()); + } + + public static Person nonExistentPerson() { + return new Person(nonExistentId(), "Jane", "Smith"); + } + + public static String newName() { + return "John"; + } + + public static String newSurname() { + return "Doe"; + } + + public static Person newPerson() { + return new Person(people().length + 1, newName(), newSurname()); + } +} diff --git a/src/test/java/es/uvigo/esei/daa/entities/PersonUnitTest.java b/src/test/java/es/uvigo/esei/daa/entities/PersonUnitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c7a3db5c038bf159b89e987098b21e7c560177e8 --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/entities/PersonUnitTest.java @@ -0,0 +1,82 @@ +package es.uvigo.esei.daa.entities; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class PersonUnitTest { + @Test + public void testPersonIntStringString() { + final int id = 1; + final String name = "John"; + final String surname = "Doe"; + + final Person person = new Person(id, name, surname); + + assertThat(person.getId(), is(equalTo(id))); + assertThat(person.getName(), is(equalTo(name))); + assertThat(person.getSurname(), is(equalTo(surname))); + } + + @Test(expected = NullPointerException.class) + public void testPersonIntStringStringNullName() { + new Person(1, null, "Doe"); + } + + @Test(expected = NullPointerException.class) + public void testPersonIntStringStringNullSurname() { + new Person(1, "John", null); + } + + @Test + public void testSetName() { + final int id = 1; + final String surname = "Doe"; + + final Person person = new Person(id, "John", surname); + person.setName("Juan"); + + assertThat(person.getId(), is(equalTo(id))); + assertThat(person.getName(), is(equalTo("Juan"))); + assertThat(person.getSurname(), is(equalTo(surname))); + } + + @Test(expected = NullPointerException.class) + public void testSetNullName() { + final Person person = new Person(1, "John", "Doe"); + + person.setName(null); + } + + @Test + public void testSetSurname() { + final int id = 1; + final String name = "John"; + + final Person person = new Person(id, name, "Doe"); + person.setSurname("Dolores"); + + assertThat(person.getId(), is(equalTo(id))); + assertThat(person.getName(), is(equalTo(name))); + assertThat(person.getSurname(), is(equalTo("Dolores"))); + } + + @Test(expected = NullPointerException.class) + public void testSetNullSurname() { + final Person person = new Person(1, "John", "Doe"); + + person.setSurname(null); + } + + @Test + public void testEqualsObject() { + final Person personA = new Person(1, "Name A", "Surname A"); + final Person personB = new Person(1, "Name B", "Surname B"); + + assertTrue(personA.equals(personB)); + } + +} diff --git a/src/test/java/es/uvigo/esei/daa/matchers/HasHttpStatus.java b/src/test/java/es/uvigo/esei/daa/matchers/HasHttpStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..03bc3e30b9b2877616cbc8e64dfa5ebceeacc122 --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/matchers/HasHttpStatus.java @@ -0,0 +1,80 @@ +/* + * #%L + * PanDrugsDB Backend + * %% + * Copyright (C) 2015 Fátima Al-Shahrour, Elena Piñeiro, Daniel Glez-Peña and Miguel Reboiro-Jato + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package es.uvigo.esei.daa.matchers; + +import static java.util.Objects.requireNonNull; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.Response.StatusType; + +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +public class HasHttpStatus extends TypeSafeMatcher { + private final StatusType expectedStatus; + + public HasHttpStatus(StatusType expectedStatus) { + this.expectedStatus = requireNonNull(expectedStatus); + } + + public HasHttpStatus(int expectedStatus) { + this(Status.fromStatusCode(expectedStatus)); + } + + @Override + public void describeTo(Description description) { + description.appendValue(this.expectedStatus); + } + + @Override + protected boolean matchesSafely(Response item) { + return item != null && expectedStatus.getStatusCode() == item.getStatusInfo().getStatusCode(); + } + + @Factory + public static Matcher hasHttpStatus(StatusType expectedStatus) { + return new HasHttpStatus(expectedStatus); + } + + @Factory + public static Matcher hasHttpStatus(int expectedStatus) { + return new HasHttpStatus(expectedStatus); + } + + @Factory + public static Matcher hasOkStatus() { + return new HasHttpStatus(Response.Status.OK); + } + + @Factory + public static Matcher hasBadRequestStatus() { + return new HasHttpStatus(Response.Status.BAD_REQUEST); + } + + @Factory + public static Matcher hasInternalServerErrorStatus() { + return new HasHttpStatus(Response.Status.INTERNAL_SERVER_ERROR); + } +} diff --git a/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToEntity.java b/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..a85dbc5c84f3c2eb2d182b7dfffc73a851be2123 --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToEntity.java @@ -0,0 +1,364 @@ +package es.uvigo.esei.daa.matchers; + +import static java.util.Arrays.asList; +import static java.util.Arrays.stream; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; +import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; +import static org.hamcrest.collection.IsIterableContainingInOrder.contains; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.StreamSupport; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/** + * An abstract {@link Matcher} that can be used to create new matchers that + * compare entities by their attributes. + * + * @author Miguel Reboiro Jato + * + * @param the type of the entities to be matched. + */ +public abstract class IsEqualToEntity extends TypeSafeMatcher { + /** + * The expected entity. + */ + protected final T expected; + + private Consumer describeTo; + + /** + * Constructs a new instance of {@link IsEqualToEntity}. + * + * @param entity the expected tentity. + */ + public IsEqualToEntity(final T entity) { + this.expected = requireNonNull(entity); + } + + @Override + public void describeTo(final Description description) { + if (this.describeTo != null) + this.describeTo.accept(description); + } + + /** + * Adds a new description using the template: + *

+ * {@code entity with value '' for } + *

+ * + * @param attribute the name of the attribute compared. + * @param expected the expected value. + */ + protected void addTemplatedDescription(final String attribute, final Object expected) { + this.describeTo = d -> d.appendText(String.format( + "%s entity with value '%s' for %s", + this.expected.getClass().getSimpleName(), + expected, attribute + )); + } + + /** + * Adds as the description of this matcher the + * {@link Matcher#describeTo(Description)} method of other matcher. + * + * @param matcher the matcher whose description will be used. + */ + protected void addMatcherDescription(final Matcher matcher) { + this.describeTo = matcher::describeTo; + } + + /** + * Cleans the current description. + */ + protected void clearDescribeTo() { + this.describeTo = null; + } + + protected boolean checkAttribute( + final String attribute, + final Function getter, final T actual, + final Function> matcherFactory + ) { + final R expectedValue = getter.apply(this.expected); + final R actualValue = getter.apply(actual); + + if (expectedValue == null && actualValue == null) { + return true; + } else if (expectedValue == null || actualValue == null) { + this.addTemplatedDescription(attribute, expectedValue); + return false; + } else { + final Matcher matcher = matcherFactory.apply(expectedValue); + if (matcher.matches(actualValue)) { + return true; + } else { + this.addMatcherDescription(matcher); + + return false; + } + } + } + + /** + * Compares the expected and the actual value of an array attribute. The + * elements of the attribute will be checked using a custom matcher. + * If the comparison fails, the description of the error will be updated. + * + * @param attribute the name of the attribute compared. + * @param getter the getter function of the attribute. + * @param actual the actual entity being compared to the expected entity. + * @param matcherFactory a function that creates a matcher for the expected + * array values. + * @param type of the value returned by the getter. + * @return {@code true} if the value of the expected and actual attributes + * are equals and {@code false} otherwise. If the result is {@code false}, + * the current description will be updated. + */ + protected boolean checkArrayAttribute( + final String attribute, + final Function getter, final T actual, + final Function>> matcherFactory + ) { + final R[] expectedValue = getter.apply(this.expected); + final R[] actualValue = getter.apply(actual); + + if (expectedValue == null && actualValue == null) { + return true; + } else if (expectedValue == null || actualValue == null) { + this.addTemplatedDescription(attribute, expectedValue); + return false; + } else { + final Matcher> matcher = + matcherFactory.apply(expectedValue); + + if (matcher.matches(asList(actualValue))) { + return true; + } else { + this.addMatcherDescription(matcher); + + return false; + } + } + } + + /** + * Compares the expected and the actual value of an iterable attribute. The + * elements of the attribute will be checked using a custom matcher. + * If the comparison fails, the description of the error will be updated. + * + * @param attribute the name of the attribute compared. + * @param getter the getter function of the attribute. + * @param actual the actual entity being compared to the expected entity. + * @param matcherFactory a function that creates a matcher for the expected + * iterable values. + * @param type of the value returned by the getter. + * @return {@code true} if the value of the expected and actual attributes + * are equals and {@code false} otherwise. If the result is {@code false}, + * the current description will be updated. + */ + protected boolean checkIterableAttribute( + final String attribute, + final Function> getter, final T actual, + final Function, Matcher>> matcherFactory + ) { + final Iterable expectedValue = getter.apply(this.expected); + final Iterable actualValue = getter.apply(actual); + + if (expectedValue == null && actualValue == null) { + return true; + } else if (expectedValue == null || actualValue == null) { + this.addTemplatedDescription(attribute, expectedValue); + return false; + } else { + final Matcher> matcher = + matcherFactory.apply(expectedValue); + + if (matcher.matches(actualValue)) { + return true; + } else { + this.addMatcherDescription(matcher); + + return false; + } + } + } + + /** + * Compares the expected and the actual value of an attribute. If the + * comparison fails, the description of the error will be updated. + * + * @param attribute the name of the attribute compared. + * @param getter the getter function of the attribute. + * @param actual the actual entity being compared to the expected entity. + * @param type of the value returned by the getter. + * @return {@code true} if the value of the expected and actual attributes + * are equals and {@code false} otherwise. If the result is {@code false}, + * the current description will be updated. + */ + protected boolean checkAttribute( + final String attribute, final Function getter, final T actual + ) { + final R expectedValue = getter.apply(this.expected); + final R actualValue = getter.apply(actual); + + if (expectedValue == null && actualValue == null) { + return true; + } else if (expectedValue == null || !expectedValue.equals(actualValue)) { + this.addTemplatedDescription(attribute, expectedValue); + return false; + } else { + return true; + } + } + + /** + * Compares the expected and the actual value of an array attribute. If the + * comparison fails, the description of the error will be updated. + * + * @param attribute the name of the attribute compared. + * @param getter the getter function of the attribute. + * @param actual the actual entity being compared to the expected entity. + * @param type of the value returned by the getter. + * @return {@code true} if the value of the expected and actual attributes + * are equals and {@code false} otherwise. If the result is {@code false}, + * the current description will be updated. + */ + protected boolean checkArrayAttribute( + final String attribute, final Function getter, final T actual + ) { + final R[] expectedValue = getter.apply(this.expected); + final R[] actualValue = getter.apply(actual); + + if (expectedValue == null && actualValue == null) { + return true; + } else if (expectedValue == null || actualValue == null) { + this.addTemplatedDescription(attribute, expectedValue == null ? "null" : Arrays.toString(expectedValue)); + return false; + } else if (!Arrays.equals(expectedValue, actualValue)) { + this.addTemplatedDescription(attribute, Arrays.toString(expectedValue)); + return false; + } else + return true; + } + + /** + * Compares the expected and the actual value of an int array attribute. If + * the comparison fails, the description of the error will be updated. + * + * @param attribute the name of the attribute compared. + * @param getter the getter function of the attribute. + * @param actual the actual entity being compared to the expected entity. + * @param type of the value returned by the getter. + * @return {@code true} if the value of the expected and actual attributes + * are equals and {@code false} otherwise. If the result is {@code false}, + * the current description will be updated. + */ + protected boolean checkIntArrayAttribute( + final String attribute, final Function getter, final T actual + ) { + final int[] expectedValue = getter.apply(this.expected); + final int[] actualValue = getter.apply(actual); + + if (expectedValue == null && actualValue == null) { + return true; + } else if (expectedValue == null || actualValue == null) { + this.addTemplatedDescription(attribute, expectedValue == null ? "null" : Arrays.toString(expectedValue)); + return false; + } else if (!Arrays.equals(expectedValue, actualValue)) { + this.addTemplatedDescription(attribute, Arrays.toString(expectedValue)); + return false; + } else + return true; + } + + /** + * Utility method that generates a {@link Matcher} that compares several + * entities. + * + * @param converter a function to create a matcher for an entity. + * @param entities the entities to be used as the expected values. + * @param type of the entity. + * @return a new {@link Matcher} that compares several entities. + */ + @SafeVarargs + protected static Matcher> containsEntityInAnyOrder( + final Function> converter, final T ... entities + ) { + final Collection> entitiesMatchers = stream(entities) + .map(converter) + .collect(toList()); + + return containsInAnyOrder(entitiesMatchers); + } + + /** + * Utility method that generates a {@link Matcher} that compares several + * entities. + * + * @param converter a function to create a matcher for an entity. + * @param entities the entities to be used as the expected values. + * @param type of the entity. + * @return a new {@link Matcher} that compares several entities. + */ + protected static Matcher> containsEntityInAnyOrder( + final Function> converter, final Iterable entities + ) { + final Collection> entitiesMatchers = + StreamSupport.stream(entities.spliterator(), false) + .map(converter) + .collect(toList()); + + return containsInAnyOrder(entitiesMatchers); + } + + /** + * Utility method that generates a {@link Matcher} that compares several + * entities in the same received order. + * + * @param converter A function to create a matcher for an entity. + * @param entities The entities to be used as the expected values, in the + * order to be compared. + * @param The type of the entity. + * + * @return A new {@link Matcher} that compares several entities in the same + * received order. + */ + @SafeVarargs + protected static Matcher> containsEntityInOrder( + final Function> converter, final T ... entities + ) { + return contains(stream(entities).map(converter).collect(toList())); + } + + /** + * Utility method that generates a {@link Matcher} that compares several + * entities in the same received order. + * + * @param converter A function to create a matcher for an entity. + * @param entities The entities to be used as the expected values, in the + * order to be compared. + * @param The type of the entity. + * + * @return A new {@link Matcher} that compares several entities in the same + * received order. + */ + protected static Matcher> containsEntityInOrder( + final Function> converter, final Iterable entities + ) { + final List> matchersList = + StreamSupport.stream(entities.spliterator(), false) + .map(converter) + .collect(toList()); + + return contains(matchersList); + } +} diff --git a/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToPerson.java b/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToPerson.java new file mode 100644 index 0000000000000000000000000000000000000000..6c06e5f0ff2b8d7e69d96da15b7af78cfd3c62e3 --- /dev/null +++ b/src/test/java/es/uvigo/esei/daa/matchers/IsEqualToPerson.java @@ -0,0 +1,56 @@ +package es.uvigo.esei.daa.matchers; + +import org.hamcrest.Factory; +import org.hamcrest.Matcher; + +import es.uvigo.esei.daa.entities.Person; + +public class IsEqualToPerson extends IsEqualToEntity { + public IsEqualToPerson(Person entity) { + super(entity); + } + + @Override + protected boolean matchesSafely(Person actual) { + this.clearDescribeTo(); + + if (actual == null) { + this.addTemplatedDescription("actual", expected.toString()); + return false; + } else { + return checkAttribute("id", Person::getId, actual) + && checkAttribute("name", Person::getName, actual) + && checkAttribute("surname", Person::getSurname, actual); + } + } + + /** + * Factory method that creates a new {@link IsEqualToEntity} matcher with + * the provided {@link Person} as the expected value. + * + * @param person the expected person. + * @return a new {@link IsEqualToEntity} matcher with the provided + * {@link Person} as the expected value. + */ + @Factory + public static IsEqualToPerson equalsToPerson(Person person) { + return new IsEqualToPerson(person); + } + + /** + * Factory method that returns a new {@link Matcher} that includes several + * {@link IsEqualToPerson} matchers, each one using an {@link Person} of the + * provided ones as the expected value. + * + * @param persons the persons to be used as the expected values. + * @return a new {@link Matcher} that includes several + * {@link IsEqualToPerson} matchers, each one using an {@link Person} of the + * provided ones as the expected value. + * @see IsEqualToEntity#containsEntityInAnyOrder(java.util.function.Function, Object...) + */ + @Factory + public static Matcher> containsPeopleInAnyOrder(Person ... persons) { + return containsEntityInAnyOrder(IsEqualToPerson::equalsToPerson, persons); + } + +} diff --git a/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceTest.java b/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceTest.java index f317e86764e9266657f7516f6a3932bbcf67fc14..3fe2f7401b5f0fe84822a35f2d5bb1d32333c12a 100644 --- a/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceTest.java +++ b/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceTest.java @@ -1,9 +1,20 @@ package es.uvigo.esei.daa.rest; -import static es.uvigo.esei.daa.TestUtils.assertBadRequestStatus; -import static es.uvigo.esei.daa.TestUtils.assertOkStatus; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentId; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newName; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newSurname; +import static es.uvigo.esei.daa.dataset.PeopleDataset.nonExistentId; +import static es.uvigo.esei.daa.dataset.PeopleDataset.people; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasBadRequestStatus; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasOkStatus; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.containsPeopleInAnyOrder; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.equalsToPerson; import static javax.ws.rs.client.Entity.entity; -import static org.junit.Assert.assertEquals; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; import java.io.IOException; import java.util.List; @@ -17,7 +28,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.glassfish.jersey.client.ClientConfig; -import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.Test; import org.junit.runner.RunWith; @@ -30,6 +40,7 @@ import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.ExpectedDatabase; +import es.uvigo.esei.daa.DAAExampleApplication; import es.uvigo.esei.daa.entities.Person; import es.uvigo.esei.daa.listeners.ApplicationContextBinding; import es.uvigo.esei.daa.listeners.ApplicationContextJndiBindingTestExecutionListener; @@ -56,15 +67,14 @@ import es.uvigo.esei.daa.listeners.DbManagementTestExecutionListener; public class PeopleResourceTest extends JerseyTest { @Override protected Application configure() { - return new ResourceConfig(PeopleResource.class) - .register(JacksonJsonProvider.class) - .property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE); + return new DAAExampleApplication(); } @Override protected void configureClient(ClientConfig config) { super.configureClient(config); + // Enables JSON transformation in client config.register(JacksonJsonProvider.class); config.property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE); } @@ -72,136 +82,145 @@ public class PeopleResourceTest extends JerseyTest { @Test public void testList() throws IOException { final Response response = target("people").request().get(); - assertOkStatus(response); + assertThat(response, hasOkStatus()); final List people = response.readEntity(new GenericType>(){}); - assertEquals(10, people.size()); + + assertThat(people, containsPeopleInAnyOrder(people())); } @Test public void testGet() throws IOException { - final Response response = target("people/4").request().get(); - assertOkStatus(response); + final Response response = target("people/" + existentId()).request().get(); + assertThat(response, hasOkStatus()); final Person person = response.readEntity(Person.class); - assertEquals(4, person.getId()); - assertEquals("María", person.getName()); - assertEquals("Márquez", person.getSurname()); + + assertThat(person, is(equalsToPerson(existentPerson()))); } @Test public void testGetInvalidId() throws IOException { - assertBadRequestStatus(target("people/100").request().get()); + final Response response = target("people/" + nonExistentId()).request().get(); + + assertThat(response, hasBadRequestStatus()); } @Test @ExpectedDatabase("/datasets/dataset-add.xml") public void testAdd() throws IOException { final Form form = new Form(); - form.param("name", "John"); - form.param("surname", "Doe"); + form.param("name", newName()); + form.param("surname", newSurname()); final Response response = target("people") .request(MediaType.APPLICATION_JSON_TYPE) .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - assertOkStatus(response); + assertThat(response, hasOkStatus()); final Person person = response.readEntity(Person.class); - assertEquals(11, person.getId()); - assertEquals("John", person.getName()); - assertEquals("Doe", person.getSurname()); + + assertThat(person, is(equalsToPerson(newPerson()))); } @Test public void testAddMissingName() throws IOException { final Form form = new Form(); - form.param("surname", "Ximénez"); + form.param("surname", newSurname()); final Response response = target("people") .request(MediaType.APPLICATION_JSON_TYPE) .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - assertBadRequestStatus(response); + assertThat(response, hasBadRequestStatus()); } @Test public void testAddMissingSurname() throws IOException { final Form form = new Form(); - form.param("name", "Xoel"); + form.param("name", newName()); final Response response = target("people") .request(MediaType.APPLICATION_JSON_TYPE) .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - assertBadRequestStatus(response); + assertThat(response, hasBadRequestStatus()); } @Test @ExpectedDatabase("/datasets/dataset-modify.xml") public void testModify() throws IOException { final Form form = new Form(); - form.param("name", "John"); - form.param("surname", "Doe"); + form.param("name", newName()); + form.param("surname", newSurname()); - final Response response = target("people/5") + final Response response = target("people/" + existentId()) .request(MediaType.APPLICATION_JSON_TYPE) .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - assertOkStatus(response); + assertThat(response, hasOkStatus()); - final Person person = response.readEntity(Person.class); - assertEquals(5, person.getId()); - assertEquals("John", person.getName()); - assertEquals("Doe", person.getSurname()); + final Person modifiedPerson = response.readEntity(Person.class); + + final Person person = existentPerson(); + person.setName(newName()); + person.setSurname(newSurname()); + + assertThat(modifiedPerson, is(equalsToPerson(person))); } @Test public void testModifyName() throws IOException { final Form form = new Form(); - form.param("name", "Marta"); + form.param("name", newName()); - final Response response = target("people/4") + final Response response = target("people/" + existentId()) .request(MediaType.APPLICATION_JSON_TYPE) .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - assertBadRequestStatus(response); + assertThat(response, hasBadRequestStatus()); } @Test public void testModifySurname() throws IOException { final Form form = new Form(); - form.param("surname", "Méndez"); + form.param("surname", newSurname()); - final Response response = target("people/4") + final Response response = target("people/" + existentId()) .request(MediaType.APPLICATION_JSON_TYPE) .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - assertBadRequestStatus(response); + assertThat(response, hasBadRequestStatus()); } @Test public void testModifyInvalidId() throws IOException { final Form form = new Form(); - form.param("name", "Marta"); - form.param("surname", "Méndez"); + form.param("name", newName()); + form.param("surname", newSurname()); - final Response response = target("people/100") + final Response response = target("people/" + nonExistentId()) .request(MediaType.APPLICATION_JSON_TYPE) .put(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); - assertBadRequestStatus(response); + assertThat(response, hasBadRequestStatus()); } @Test @ExpectedDatabase("/datasets/dataset-delete.xml") public void testDelete() throws IOException { - final Response response = target("people/4").request().delete(); - assertOkStatus(response); + final Response response = target("people/" + existentId()).request().delete(); + + assertThat(response, hasOkStatus()); - assertEquals(4, (int) response.readEntity(Integer.class)); + final Integer deletedId = response.readEntity(Integer.class); + + assertThat(deletedId, is(equalTo(existentId()))); } @Test public void testDeleteInvalidId() throws IOException { - assertBadRequestStatus(target("people/100").request().delete()); + final Response response = target("people/" + nonExistentId()).request().delete(); + + assertThat(response, hasBadRequestStatus()); } } diff --git a/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceUnitTest.java b/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceUnitTest.java index a342a6c234c82dc3cf7169ee5a3542d5889c5bf8..1e7d8af382dfec17a8786063c236351c70673546 100644 --- a/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceUnitTest.java +++ b/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceUnitTest.java @@ -1,5 +1,17 @@ package es.uvigo.esei.daa.rest; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentId; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newName; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newSurname; +import static es.uvigo.esei.daa.dataset.PeopleDataset.people; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasBadRequestStatus; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasInternalServerErrorStatus; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasOkStatus; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.containsPeopleInAnyOrder; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.equalsToPerson; +import static java.util.Arrays.asList; import static org.easymock.EasyMock.anyInt; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.anyString; @@ -8,13 +20,13 @@ import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; -import java.util.Arrays; import java.util.List; import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; import org.junit.After; import org.junit.Before; @@ -45,77 +57,88 @@ public class PeopleResourceUnitTest { } @Test + @SuppressWarnings("unchecked") public void testList() throws Exception { - final List people = Arrays.asList( - new Person(1, "Pepe", "Pérez"), - new Person(2, "Paco", "Martínez"), - new Person(3, "Martina", "Juárez") - ); + final List people = asList(people()); expect(daoMock.list()).andReturn(people); + replay(daoMock); final Response response = resource.list(); - assertEquals(people, response.getEntity()); - assertEquals(Status.OK, response.getStatusInfo()); + + assertThat(response, hasOkStatus()); + assertThat((List) response.getEntity(), containsPeopleInAnyOrder(people())); } @Test public void testListDAOException() throws Exception { expect(daoMock.list()).andThrow(new DAOException()); + replay(daoMock); final Response response = resource.list(); - assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo()); + + assertThat(response, hasInternalServerErrorStatus()); } @Test public void testGet() throws Exception { - final Person person = new Person(1, "Pepe", "Pérez"); + final Person person = existentPerson(); expect(daoMock.get(person.getId())).andReturn(person); + replay(daoMock); final Response response = resource.get(person.getId()); - assertEquals(person, response.getEntity()); - assertEquals(Status.OK, response.getStatusInfo()); + + assertThat(response, hasOkStatus()); + assertThat((Person) response.getEntity(), is(equalsToPerson(person))); } @Test public void testGetDAOException() throws Exception { expect(daoMock.get(anyInt())).andThrow(new DAOException()); + replay(daoMock); - final Response response = resource.get(1); - assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo()); + final Response response = resource.get(existentId()); + + assertThat(response, hasInternalServerErrorStatus()); } @Test public void testGetIllegalArgumentException() throws Exception { expect(daoMock.get(anyInt())).andThrow(new IllegalArgumentException()); + replay(daoMock); - final Response response = resource.get(1); - assertEquals(Status.BAD_REQUEST, response.getStatusInfo()); + final Response response = resource.get(existentId()); + + assertThat(response, hasBadRequestStatus()); } @Test public void testDelete() throws Exception { daoMock.delete(anyInt()); + replay(daoMock); final Response response = resource.delete(1); - assertEquals(Status.OK, response.getStatusInfo()); + + assertThat(response, hasOkStatus()); } @Test public void testDeleteDAOException() throws Exception { daoMock.delete(anyInt()); expectLastCall().andThrow(new DAOException()); + replay(daoMock); final Response response = resource.delete(1); - assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo()); + + assertThat(response, hasInternalServerErrorStatus()); } @Test @@ -125,12 +148,15 @@ public class PeopleResourceUnitTest { replay(daoMock); final Response response = resource.delete(1); - assertEquals(Status.BAD_REQUEST, response.getStatusInfo()); + + assertThat(response, hasBadRequestStatus()); } @Test public void testModify() throws Exception { - final Person person = new Person(1, "Pepe", "Pérez"); + final Person person = existentPerson(); + person.setName(newName()); + person.setSurname(newSurname()); daoMock.modify(person); @@ -139,8 +165,8 @@ public class PeopleResourceUnitTest { final Response response = resource.modify( person.getId(), person.getName(), person.getSurname()); + assertThat(response, hasOkStatus()); assertEquals(person, response.getEntity()); - assertEquals(Status.OK, response.getStatusInfo()); } @Test @@ -150,8 +176,9 @@ public class PeopleResourceUnitTest { replay(daoMock); - final Response response = resource.modify(1, "Paco", "Pérez"); - assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo()); + final Response response = resource.modify(existentId(), newName(), newSurname()); + + assertThat(response, hasInternalServerErrorStatus()); } @Test @@ -160,24 +187,23 @@ public class PeopleResourceUnitTest { expectLastCall().andThrow(new IllegalArgumentException()); replay(daoMock); + + final Response response = resource.modify(existentId(), newName(), newSurname()); - final Response response = resource.modify(1, "Paco", "Pérez"); - assertEquals(Status.BAD_REQUEST, response.getStatusInfo()); + assertThat(response, hasBadRequestStatus()); } @Test public void testAdd() throws Exception { - final Person person = new Person(1, "Pepe", "Pérez"); - - expect(daoMock.add(person.getName(), person.getSurname())) - .andReturn(person); + expect(daoMock.add(newName(), newSurname())) + .andReturn(newPerson()); replay(daoMock); - final Response response = resource.add( - person.getName(), person.getSurname()); - assertEquals(person, response.getEntity()); - assertEquals(Status.OK, response.getStatusInfo()); + final Response response = resource.add(newName(), newSurname()); + + assertThat(response, hasOkStatus()); + assertThat((Person) response.getEntity(), is(equalsToPerson(newPerson()))); } @Test @@ -186,8 +212,9 @@ public class PeopleResourceUnitTest { .andThrow(new DAOException()); replay(daoMock); - final Response response = resource.add("Paco", "Pérez"); - assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo()); + final Response response = resource.add(newName(), newSurname()); + + assertThat(response, hasInternalServerErrorStatus()); } @Test @@ -196,7 +223,8 @@ public class PeopleResourceUnitTest { .andThrow(new IllegalArgumentException()); replay(daoMock); - final Response response = resource.add("Paco", "Pérez"); - assertEquals(Status.BAD_REQUEST, response.getStatusInfo()); + final Response response = resource.add(newName(), newSurname()); + + assertThat(response, hasBadRequestStatus()); } } diff --git a/src/test/java/es/uvigo/esei/daa/suites/UnitTestSuite.java b/src/test/java/es/uvigo/esei/daa/suites/UnitTestSuite.java index fffda9e014e1500799fbe6b71dffca61a75e5c5e..a21ca3663b624f6cf2ea326a347de55ae8f96ca9 100644 --- a/src/test/java/es/uvigo/esei/daa/suites/UnitTestSuite.java +++ b/src/test/java/es/uvigo/esei/daa/suites/UnitTestSuite.java @@ -5,9 +5,11 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import es.uvigo.esei.daa.dao.PeopleDAOUnitTest; +import es.uvigo.esei.daa.entities.PersonUnitTest; import es.uvigo.esei.daa.rest.PeopleResourceUnitTest; -@SuiteClasses({ +@SuiteClasses({ + PersonUnitTest.class, PeopleDAOUnitTest.class, PeopleResourceUnitTest.class }) diff --git a/src/test/java/es/uvigo/esei/daa/TestUtils.java b/src/test/java/es/uvigo/esei/daa/util/ContextUtils.java similarity index 52% rename from src/test/java/es/uvigo/esei/daa/TestUtils.java rename to src/test/java/es/uvigo/esei/daa/util/ContextUtils.java index 50b51fcfe669458fc80bbb912afaa4d418c2ef85..331044f0f1f201bf6d93b2805d08ac4c85031b4c 100644 --- a/src/test/java/es/uvigo/esei/daa/TestUtils.java +++ b/src/test/java/es/uvigo/esei/daa/util/ContextUtils.java @@ -1,18 +1,15 @@ -package es.uvigo.esei.daa; - -import static org.junit.Assert.assertEquals; +package es.uvigo.esei.daa.util; import javax.naming.NamingException; import javax.sql.DataSource; -import javax.ws.rs.core.Response; import org.springframework.mock.jndi.SimpleNamingContextBuilder; -public final class TestUtils { +public final class ContextUtils { private final static SimpleNamingContextBuilder CONTEXT_BUILDER = new SimpleNamingContextBuilder(); - private TestUtils() {} + private ContextUtils() {} public static void createFakeContext(DataSource datasource) throws IllegalStateException, NamingException { @@ -24,12 +21,4 @@ public final class TestUtils { CONTEXT_BUILDER.clear(); CONTEXT_BUILDER.deactivate(); } - - public static void assertOkStatus(final Response response) { - assertEquals("Unexpected status code", Response.Status.OK.getStatusCode(), response.getStatus()); - } - - public static void assertBadRequestStatus(final Response response) { - assertEquals("Unexpected status code", Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); - } } diff --git a/src/test/java/es/uvigo/esei/daa/DatabaseQueryUnitTest.java b/src/test/java/es/uvigo/esei/daa/util/DatabaseQueryUnitTest.java similarity index 59% rename from src/test/java/es/uvigo/esei/daa/DatabaseQueryUnitTest.java rename to src/test/java/es/uvigo/esei/daa/util/DatabaseQueryUnitTest.java index a78f0bdbe837b932692875ecd894af3b29169302..e541cb7ce2edb3b91ed6c40164b296ef10a3f34c 100644 --- a/src/test/java/es/uvigo/esei/daa/DatabaseQueryUnitTest.java +++ b/src/test/java/es/uvigo/esei/daa/util/DatabaseQueryUnitTest.java @@ -1,4 +1,4 @@ -package es.uvigo.esei.daa; +package es.uvigo.esei.daa.util; import static org.easymock.EasyMock.anyString; import static org.easymock.EasyMock.createMock; @@ -18,6 +18,15 @@ import org.junit.Before; import com.mysql.jdbc.PreparedStatement; +/** + * Super-class for unit tests in the DAO layer. + * + *

The default {@link DatabaseQueryUnitTest#setUp()} method in this class + * create mocks for the datasource, connection, statement, and result variables + * that can be used by the DAO object under test.

+ * + * @author Miguel Reboiro Jato + */ public abstract class DatabaseQueryUnitTest { protected DataSource datasource; protected Connection connection; @@ -26,6 +35,11 @@ public abstract class DatabaseQueryUnitTest { protected boolean verify; + /** + * Configures the mocks and enables the verification. + * + * @throws Exception if an error happens while configuring the mocks. + */ @Before public void setUp() throws Exception { datasource = createMock(DataSource.class); @@ -36,31 +50,46 @@ public abstract class DatabaseQueryUnitTest { expect(datasource.getConnection()) .andReturn(connection); expect(connection.prepareStatement(anyString())) - .andReturn(statement); + .andReturn(statement) + .anyTimes(); // statement is optional expect(statement.executeQuery()) .andReturn(result) - .anyTimes(); // executeQuery is optional; + .anyTimes(); // executeQuery is optional statement.close(); connection.close(); verify = true; } + /** + * Removes the default behavior of the mock instances and disables the mock + * verification. + */ protected void resetAll() { reset(result, statement, connection, datasource); verify = false; } - + + /** + * Replays the configured behavior of the mock instances and enables the + * mock verification. The mocked datasource is also added to a new context. + */ protected void replayAll() throws Exception { replay(result, statement, connection, datasource); + verify = true; - TestUtils.createFakeContext(datasource); + ContextUtils.createFakeContext(datasource); } + /** + * Clears the context and verifies the mocks if the verification is enabled. + * + * @throws Exception if an error happens during verification. + */ @After public void tearDown() throws Exception { - TestUtils.clearContextBuilder(); + ContextUtils.clearContextBuilder(); try { if (verify) { diff --git a/src/test/java/es/uvigo/esei/daa/web/PeopleWebTest.java b/src/test/java/es/uvigo/esei/daa/web/PeopleWebTest.java index fbd1e5e718e29458eeb69c5c3bc0875f59c6ff89..4fc50b65dd2dbca6986c90f7f048c4cfc16d8623 100644 --- a/src/test/java/es/uvigo/esei/daa/web/PeopleWebTest.java +++ b/src/test/java/es/uvigo/esei/daa/web/PeopleWebTest.java @@ -1,7 +1,16 @@ package es.uvigo.esei.daa.web; -import static org.junit.Assert.assertEquals; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentId; +import static es.uvigo.esei.daa.dataset.PeopleDataset.existentPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newName; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newPerson; +import static es.uvigo.esei.daa.dataset.PeopleDataset.newSurname; +import static es.uvigo.esei.daa.dataset.PeopleDataset.people; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.containsPeopleInAnyOrder; +import static es.uvigo.esei.daa.matchers.IsEqualToPerson.equalsToPerson; +import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import java.util.concurrent.TimeUnit; @@ -78,42 +87,36 @@ public class PeopleWebTest { @Test public void testList() throws Exception { - assertEquals(10, mainPage.countPeople()); + assertThat(mainPage.listPeople(), containsPeopleInAnyOrder(people())); } @Test @ExpectedDatabase("/datasets/dataset-add.xml") public void testAdd() throws Exception { - final String name = "John"; - final String surname = "Doe"; + final Person newPerson = mainPage.addPerson(newName(), newSurname()); - final Person newPerson = mainPage.addPerson(name, surname); - - assertEquals(name, newPerson.getName()); - assertEquals(surname, newPerson.getSurname()); + assertThat(newPerson, is(equalsToPerson(newPerson()))); } @Test @ExpectedDatabase("/datasets/dataset-modify.xml") public void testEdit() throws Exception { - final int id = 5; - final String newName = "John"; - final String newSurname = "Doe"; + final Person person = existentPerson(); + person.setName(newName()); + person.setSurname(newSurname()); - mainPage.editPerson(id, "John", "Doe"); + mainPage.editPerson(person); - final Person person = mainPage.getPerson(id); + final Person webPerson = mainPage.getPerson(person.getId()); - assertEquals(id, person.getId()); - assertEquals(newName, person.getName()); - assertEquals(newSurname, person.getSurname()); + assertThat(webPerson, is(equalsToPerson(person))); } @Test @ExpectedDatabase("/datasets/dataset-delete.xml") public void testDelete() throws Exception { - mainPage.deletePerson(4); + mainPage.deletePerson(existentId()); - assertFalse(mainPage.hasPerson(4)); + assertFalse(mainPage.hasPerson(existentId())); } } diff --git a/src/test/java/es/uvigo/esei/daa/web/pages/MainPage.java b/src/test/java/es/uvigo/esei/daa/web/pages/MainPage.java index cd8428e80a7209ce17b3fa4a3ab578ad9004ec80..9aa4d32db07aac8721f4f6a9abb7e21f6ce0dd0a 100644 --- a/src/test/java/es/uvigo/esei/daa/web/pages/MainPage.java +++ b/src/test/java/es/uvigo/esei/daa/web/pages/MainPage.java @@ -1,5 +1,6 @@ package es.uvigo.esei.daa.web.pages; +import static java.util.stream.Collectors.toList; import static org.openqa.selenium.support.ui.ExpectedConditions.presenceOfElementLocated; import static org.openqa.selenium.support.ui.ExpectedConditions.textToBePresentInElement; @@ -42,6 +43,10 @@ public class MainPage { return new PeopleTable(this.driver).countPeople(); } + public List listPeople() { + return new PeopleTable(this.driver).listPeople(); + } + public Person getLastPerson() { return new PeopleTable(this.driver).getPersonInLastRow(); } @@ -66,13 +71,13 @@ public class MainPage { return table.getPerson(name, surname); } - public void editPerson(int id, String newName, String newSurname) { + public void editPerson(Person person) { final PeopleTable table = new PeopleTable(this.driver); - table.editPerson(id); + table.editPerson(person.getId()); final PersonForm form = new PersonForm(this.driver); - form.setName(newName); - form.setSurname(newSurname); + form.setName(person.getName()); + form.setSurname(person.getSurname()); form.submit(); } @@ -83,7 +88,6 @@ public class MainPage { } private final static class PeopleTable { - private final WebDriver driver; private final WebElement table; @@ -146,12 +150,19 @@ public class MainPage { } public int countPeople() { + return getRows().size(); + } + + public List listPeople() { + return getRows().stream() + .map(this::rowToPerson) + .collect(toList()); + } + + private List getRows() { final String xpathQuery = "//tr[starts-with(@id, '" + ID_PREFIX + "')]"; - final List peopleRows = - this.table.findElements(By.xpath(xpathQuery)); - - return peopleRows.size(); + return this.table.findElements(By.xpath(xpathQuery)); } private Person rowToPerson(WebElement row) { diff --git a/src/test/resources/contexts/mem-context.xml b/src/test/resources/contexts/mem-context.xml index dc250b9a0db01f97bfbad33d9c824f94e880e307..a37be3358a4bdc5d6e9e630b12210a2da842f9d2 100644 --- a/src/test/resources/contexts/mem-context.xml +++ b/src/test/resources/contexts/mem-context.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - + diff --git a/src/test/resources/datasets/dataset-delete.xml b/src/test/resources/datasets/dataset-delete.xml index 40470fabca572647ad7ea2750adc54acd580490d..51b0b21175dba6d713466042cf4c73ebc3f9d556 100644 --- a/src/test/resources/datasets/dataset-delete.xml +++ b/src/test/resources/datasets/dataset-delete.xml @@ -5,7 +5,7 @@ - +