Commit 234b3fb9 authored by Administrator's avatar Administrator

Refactorizes and improves the tests

The tests classes have been simplified by delegating some of the
functions in the test classes to external utility classes. For example,
a PeopleDataset has been added to manage the dataset or the
IsEqualToEntity Hamcrest matcher has been added to simplify entity
comparisons.

In addition, a PersonUnitTest class has been added to test the Person
entity.
parent bf91733b
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
<!-- Tests dependencies --> <!-- Tests dependencies -->
<junit.version>4.12</junit.version> <junit.version>4.12</junit.version>
<java-hamcrest.version>2.0.0.0</java-hamcrest.version>
<easymock.version>3.4</easymock.version> <easymock.version>3.4</easymock.version>
<selenium.java.version>2.49.1</selenium.java.version> <selenium.java.version>2.49.1</selenium.java.version>
<spring.test.version>4.2.4.RELEASE</spring.test.version> <spring.test.version>4.2.4.RELEASE</spring.test.version>
...@@ -45,7 +46,7 @@ ...@@ -45,7 +46,7 @@
<failsafe.version>2.19.1</failsafe.version> <failsafe.version>2.19.1</failsafe.version>
<maven.war.plugin.version>2.6</maven.war.plugin.version> <maven.war.plugin.version>2.6</maven.war.plugin.version>
<tomcat.maven.plugin.version>2.2</tomcat.maven.plugin.version> <tomcat.maven.plugin.version>2.2</tomcat.maven.plugin.version>
<jacoco.version>0.7.5.201505241946</jacoco.version> <jacoco.version>0.7.6.201602180812</jacoco.version>
<cargo-maven2-plugin.version>1.4.18</cargo-maven2-plugin.version> <cargo-maven2-plugin.version>1.4.18</cargo-maven2-plugin.version>
</properties> </properties>
...@@ -83,6 +84,12 @@ ...@@ -83,6 +84,12 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>java-hamcrest</artifactId>
<version>${java-hamcrest.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.easymock</groupId> <groupId>org.easymock</groupId>
<artifactId>easymock</artifactId> <artifactId>easymock</artifactId>
......
package es.uvigo.esei.daa.dao; 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; import javax.sql.DataSource;
...@@ -46,53 +58,53 @@ public class PeopleDAOTest { ...@@ -46,53 +58,53 @@ public class PeopleDAOTest {
this.dao = new PeopleDAO(); this.dao = new PeopleDAO();
} }
@Test
public void testList() throws DAOException {
assertThat(this.dao.list(), containsPeopleInAnyOrder(people()));
}
@Test @Test
public void testGet() throws DAOException { public void testGet() throws DAOException {
final Person person = this.dao.get(4); final Person person = this.dao.get(existentId());
assertEquals(4, person.getId()); assertThat(person, is(equalsToPerson(existentPerson())));
assertEquals("María", person.getName());
assertEquals("Márquez", person.getSurname());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testGetNonExistentId() throws DAOException { public void testGetNonExistentId() throws DAOException {
this.dao.get(100); this.dao.get(nonExistentId());
}
@Test
public void testList() throws DAOException {
assertEquals(10, this.dao.list().size());
} }
@Test @Test
@ExpectedDatabase("/datasets/dataset-delete.xml") @ExpectedDatabase("/datasets/dataset-delete.xml")
public void testDelete() throws DAOException { public void testDelete() throws DAOException {
this.dao.delete(4); this.dao.delete(existentId());
assertEquals(9, this.dao.list().size()); assertThat(this.dao.list(), containsPeopleInAnyOrder(peopleWithout(existentId())));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testDeleteNonExistentId() throws DAOException { public void testDeleteNonExistentId() throws DAOException {
this.dao.delete(100); this.dao.delete(nonExistentId());
} }
@Test @Test
@ExpectedDatabase("/datasets/dataset-modify.xml") @ExpectedDatabase("/datasets/dataset-modify.xml")
public void testModify() throws DAOException { 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()); assertThat(persistentPerson, is(equalsToPerson(person)));
assertEquals("John", person.getName());
assertEquals("Doe", person.getSurname());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testModifyNonExistentId() throws DAOException { public void testModifyNonExistentId() throws DAOException {
this.dao.modify(new Person(100, "John", "Doe")); this.dao.modify(nonExistentPerson());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
...@@ -103,25 +115,22 @@ public class PeopleDAOTest { ...@@ -103,25 +115,22 @@ public class PeopleDAOTest {
@Test @Test
@ExpectedDatabase("/datasets/dataset-add.xml") @ExpectedDatabase("/datasets/dataset-add.xml")
public void testAdd() throws DAOException { 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()); assertThat(person, is(equalsToPerson(newPerson())));
assertEquals("Doe", person.getSurname());
final Person personGet = this.dao.get(person.getId()); final Person persistentPerson = this.dao.get(person.getId());
assertEquals(person.getId(), personGet.getId()); assertThat(persistentPerson, is(equalsToPerson(newPerson())));
assertEquals("John", personGet.getName());
assertEquals("Doe", personGet.getSurname());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testAddNullName() throws DAOException { public void testAddNullName() throws DAOException {
this.dao.add(null, "Doe"); this.dao.add(null, newSurname());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void testAddNullSurname() throws DAOException { public void testAddNullSurname() throws DAOException {
this.dao.add("John", null); this.dao.add(newName(), null);
} }
} }
package es.uvigo.esei.daa.dao; 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.anyString;
import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.reset; 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.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import org.junit.Test; 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.entities.Person;
import es.uvigo.esei.daa.util.DatabaseQueryUnitTest;
public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
@Test @Test
public void testGet() throws Exception { public void testList() throws Exception {
final Person person = new Person(1, "Pepe", "Pérez"); final Person[] people = people();
expect(result.next()).andReturn(true); for (Person person : people) {
expect(result.getInt("id")).andReturn(person.getId()); expectPersonRow(person);
expect(result.getString("name")).andReturn(person.getName()); }
expect(result.getString("surname")).andReturn(person.getSurname()); expect(result.next()).andReturn(false);
result.close(); result.close();
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
assertEquals("Unexpected person data", assertThat(peopleDAO.list(), containsPeopleInAnyOrder(people));
person, peopleDAO.get(person.getId())
);
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = DAOException.class)
public void testGetMissing() throws Exception { public void testListUnexpectedException() throws Exception {
expect(result.next()).andReturn(false); expect(result.next()).andThrow(new SQLException());
result.close(); result.close();
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.get(2); peopleDAO.list();
} }
@Test(expected = DAOException.class) @Test
public void testGetUnexpectedException() throws Exception { public void testGet() throws Exception {
expect(result.next()).andThrow(new SQLException()); final Person existentPerson = existentPerson();
expectPersonRow(existentPerson);
result.close(); result.close();
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.get(2);
}
@Test
public void testList() throws Exception {
final List<Person> 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) { assertThat(peopleDAO.get(existentId()), is(equalTo(existentPerson)));
expect(result.next()).andReturn(true); }
expect(result.getInt("id")).andReturn(person.getId());
expect(result.getString("name")).andReturn(person.getName()); @Test(expected = IllegalArgumentException.class)
expect(result.getString("surname")).andReturn(person.getSurname()); public void testGetMissing() throws Exception {
}
expect(result.next()).andReturn(false); expect(result.next()).andReturn(false);
result.close(); result.close();
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.get(existentId());
assertEquals("Unexpected people data",
people, peopleDAO.list()
);
} }
@Test(expected = DAOException.class) @Test(expected = DAOException.class)
public void testListUnexpectedException() throws Exception { public void testGetUnexpectedException() throws Exception {
expect(result.next()).andThrow(new SQLException()); expect(result.next()).andThrow(new SQLException());
result.close(); result.close();
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.list(); peopleDAO.get(existentId());
} }
@Test @Test
public void testAdd() throws Exception { public void testAdd() throws Exception {
final Person person = new Person(1, "Pepe", "Pérez"); final Person person = newPerson();
reset(connection); reset(connection);
expect(connection.prepareStatement(anyString(), eq(1))) expect(connection.prepareStatement(anyString(), eq(Statement.RETURN_GENERATED_KEYS)))
.andReturn(statement); .andReturn(statement);
expect(statement.executeUpdate()).andReturn(1); expect(statement.executeUpdate()).andReturn(1);
expect(statement.getGeneratedKeys()).andReturn(result); expect(statement.getGeneratedKeys()).andReturn(result);
// Key retrieval
expect(result.next()).andReturn(true); expect(result.next()).andReturn(true);
expect(result.getInt(1)).andReturn(person.getId()); // Key retrieval expect(result.getInt(1)).andReturn(person.getId());
connection.close(); connection.close();
result.close(); result.close();
...@@ -112,7 +109,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -112,7 +109,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
final Person newPerson = peopleDAO.add(person.getName(), person.getSurname()); final Person newPerson = peopleDAO.add(person.getName(), person.getSurname());
assertEquals(person, newPerson); assertThat(newPerson, is(equalsToPerson(person)));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
...@@ -123,7 +120,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -123,7 +120,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
resetAll(); // No expectations resetAll(); // No expectations
peopleDAO.add(null, "Pepe"); peopleDAO.add(null, newSurname());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
...@@ -134,7 +131,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -134,7 +131,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
resetAll(); // No expectations resetAll(); // No expectations
peopleDAO.add("Pepe", null); peopleDAO.add(newName(), null);
} }
@Test(expected = DAOException.class) @Test(expected = DAOException.class)
...@@ -148,7 +145,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -148,7 +145,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.add("Paco", "Pérez"); peopleDAO.add(newName(), newSurname());
} }
@Test(expected = DAOException.class) @Test(expected = DAOException.class)
...@@ -165,7 +162,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -165,7 +162,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.add("Paco", "Pérez"); peopleDAO.add(newName(), newSurname());
} }
@Test(expected = DAOException.class) @Test(expected = DAOException.class)
...@@ -179,7 +176,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -179,7 +176,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.add("Paco", "Pérez"); peopleDAO.add(newName(), newSurname());
} }
@Test @Test
...@@ -189,7 +186,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -189,7 +186,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.delete(1); peopleDAO.delete(existentId());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
...@@ -199,7 +196,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -199,7 +196,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.delete(1); peopleDAO.delete(existentId());
} }
@Test(expected = DAOException.class) @Test(expected = DAOException.class)
...@@ -209,19 +206,17 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -209,19 +206,17 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.delete(1); peopleDAO.delete(existentId());
} }
@Test @Test
public void testModify() throws Exception { public void testModify() throws Exception {
final Person person = new Person(1, "Pepe", "Pérez");
expect(statement.executeUpdate()).andReturn(1); expect(statement.executeUpdate()).andReturn(1);
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.modify(person); peopleDAO.modify(existentPerson());
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
...@@ -242,7 +237,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -242,7 +237,7 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); final PeopleDAO peopleDAO = new PeopleDAO();
peopleDAO.modify(new Person(1, "Paco", "Pérez")); peopleDAO.modify(existentPerson());
} }
@Test(expected = DAOException.class) @Test(expected = DAOException.class)
...@@ -252,6 +247,13 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest { ...@@ -252,6 +247,13 @@ public class PeopleDAOUnitTest extends DatabaseQueryUnitTest {
replayAll(); replayAll();
final PeopleDAO peopleDAO = new PeopleDAO(); 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());
} }
} }
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<Person> 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());
}
}
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));
}
}
/*
* #%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
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #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<Response> {
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<Response> hasHttpStatus(StatusType expectedStatus) {
return new HasHttpStatus(expectedStatus);
}
@Factory
public static Matcher<Response> hasHttpStatus(int expectedStatus) {
return new HasHttpStatus(expectedStatus);
}
@Factory
public static Matcher<Response> hasOkStatus() {
return new HasHttpStatus(Response.Status.OK);
}
@Factory
public static Matcher<Response> hasBadRequestStatus() {
return new HasHttpStatus(Response.Status.BAD_REQUEST);
}
@Factory
public static Matcher<Response> hasInternalServerErrorStatus() {
return new HasHttpStatus(Response.Status.INTERNAL_SERVER_ERROR);
}
}
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 <T> the type of the entities to be matched.
*/
public abstract class IsEqualToEntity<T> extends TypeSafeMatcher<T> {
/**
* The expected entity.
*/
protected final T expected;
private Consumer<Description> 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:
* <p>
* {@code <expected class> entity with value '<expected>' for <attribute>}
* </p>
*
* @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 <R> boolean checkAttribute(
final String attribute,
final Function<T, R> getter, final T actual,
final Function<R, Matcher<R>> 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<R> 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 <R> 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 <R> boolean checkArrayAttribute(
final String attribute,
final Function<T, R[]> getter, final T actual,
final Function<R[], Matcher<Iterable<? extends R>>> 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<Iterable<? extends R>> 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 <R> 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 <R> boolean checkIterableAttribute(
final String attribute,
final Function<T, Iterable<R>> getter, final T actual,
final Function<Iterable<R>, Matcher<Iterable<? extends R>>> matcherFactory
) {
final Iterable<R> expectedValue = getter.apply(this.expected);
final Iterable<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<Iterable<? extends R>> 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 <R> 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 <R> boolean checkAttribute(
final String attribute, final Function<T, R> 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 <R> 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 <R> boolean checkArrayAttribute(
final String attribute, final Function<T, R[]> 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 <R> 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<T, int[]> 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 <T> type of the entity.
* @return a new {@link Matcher} that compares several entities.
*/
@SafeVarargs
protected static <T> Matcher<Iterable<? extends T>> containsEntityInAnyOrder(
final Function<T, Matcher<? super T>> converter, final T ... entities
) {
final Collection<Matcher<? super T>> 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 <T> type of the entity.
* @return a new {@link Matcher} that compares several entities.
*/
protected static <T> Matcher<Iterable<? extends T>> containsEntityInAnyOrder(
final Function<T, Matcher<? super T>> converter, final Iterable<T> entities
) {
final Collection<Matcher<? super T>> 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 <T> The type of the entity.
*
* @return A new {@link Matcher} that compares several entities in the same
* received order.
*/
@SafeVarargs
protected static <T> Matcher<Iterable<? extends T>> containsEntityInOrder(
final Function<T, Matcher<? super T>> 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 <T> The type of the entity.
*
* @return A new {@link Matcher} that compares several entities in the same
* received order.
*/
protected static <T> Matcher<Iterable<? extends T>> containsEntityInOrder(
final Function<T, Matcher<? super T>> converter, final Iterable<T> entities
) {
final List<Matcher<? super T>> matchersList =
StreamSupport.stream(entities.spliterator(), false)
.map(converter)
.collect(toList());
return contains(matchersList);
}
}
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<Person> {
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<Iterable<? extends Person>> containsPeopleInAnyOrder(Person ... persons) {
return containsEntityInAnyOrder(IsEqualToPerson::equalsToPerson, persons);
}
}
package es.uvigo.esei.daa.rest; package es.uvigo.esei.daa.rest;
import static es.uvigo.esei.daa.TestUtils.assertBadRequestStatus; import static es.uvigo.esei.daa.dataset.PeopleDataset.existentId;
import static es.uvigo.esei.daa.TestUtils.assertOkStatus; 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 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.io.IOException;
import java.util.List; import java.util.List;
...@@ -17,7 +28,6 @@ import javax.ws.rs.core.MediaType; ...@@ -17,7 +28,6 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest; import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -30,6 +40,7 @@ import com.github.springtestdbunit.DbUnitTestExecutionListener; ...@@ -30,6 +40,7 @@ import com.github.springtestdbunit.DbUnitTestExecutionListener;
import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.github.springtestdbunit.annotation.ExpectedDatabase; import com.github.springtestdbunit.annotation.ExpectedDatabase;
import es.uvigo.esei.daa.DAAExampleApplication;
import es.uvigo.esei.daa.entities.Person; import es.uvigo.esei.daa.entities.Person;
import es.uvigo.esei.daa.listeners.ApplicationContextBinding; import es.uvigo.esei.daa.listeners.ApplicationContextBinding;
import es.uvigo.esei.daa.listeners.ApplicationContextJndiBindingTestExecutionListener; import es.uvigo.esei.daa.listeners.ApplicationContextJndiBindingTestExecutionListener;
...@@ -56,15 +67,14 @@ import es.uvigo.esei.daa.listeners.DbManagementTestExecutionListener; ...@@ -56,15 +67,14 @@ import es.uvigo.esei.daa.listeners.DbManagementTestExecutionListener;
public class PeopleResourceTest extends JerseyTest { public class PeopleResourceTest extends JerseyTest {
@Override @Override
protected Application configure() { protected Application configure() {
return new ResourceConfig(PeopleResource.class) return new DAAExampleApplication();
.register(JacksonJsonProvider.class)
.property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE);
} }
@Override @Override
protected void configureClient(ClientConfig config) { protected void configureClient(ClientConfig config) {
super.configureClient(config); super.configureClient(config);
// Enables JSON transformation in client
config.register(JacksonJsonProvider.class); config.register(JacksonJsonProvider.class);
config.property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE); config.property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE);
} }
...@@ -72,136 +82,145 @@ public class PeopleResourceTest extends JerseyTest { ...@@ -72,136 +82,145 @@ public class PeopleResourceTest extends JerseyTest {
@Test @Test
public void testList() throws IOException { public void testList() throws IOException {
final Response response = target("people").request().get(); final Response response = target("people").request().get();
assertOkStatus(response); assertThat(response, hasOkStatus());
final List<Person> people = response.readEntity(new GenericType<List<Person>>(){}); final List<Person> people = response.readEntity(new GenericType<List<Person>>(){});
assertEquals(10, people.size());
assertThat(people, containsPeopleInAnyOrder(people()));
} }
@Test @Test
public void testGet() throws IOException { public void testGet() throws IOException {
final Response response = target("people/4").request().get(); final Response response = target("people/" + existentId()).request().get();
assertOkStatus(response); assertThat(response, hasOkStatus());
final Person person = response.readEntity(Person.class); final Person person = response.readEntity(Person.class);
assertEquals(4, person.getId());
assertEquals("María", person.getName()); assertThat(person, is(equalsToPerson(existentPerson())));
assertEquals("Márquez", person.getSurname());
} }
@Test @Test
public void testGetInvalidId() throws IOException { public void testGetInvalidId() throws IOException {
assertBadRequestStatus(target("people/100").request().get()); final Response response = target("people/" + nonExistentId()).request().get();
assertThat(response, hasBadRequestStatus());
} }
@Test @Test
@ExpectedDatabase("/datasets/dataset-add.xml") @ExpectedDatabase("/datasets/dataset-add.xml")
public void testAdd() throws IOException { public void testAdd() throws IOException {
final Form form = new Form(); final Form form = new Form();
form.param("name", "John"); form.param("name", newName());
form.param("surname", "Doe"); form.param("surname", newSurname());
final Response response = target("people") final Response response = target("people")
.request(MediaType.APPLICATION_JSON_TYPE) .request(MediaType.APPLICATION_JSON_TYPE)
.post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
assertOkStatus(response); assertThat(response, hasOkStatus());
final Person person = response.readEntity(Person.class); final Person person = response.readEntity(Person.class);
assertEquals(11, person.getId());
assertEquals("John", person.getName()); assertThat(person, is(equalsToPerson(newPerson())));
assertEquals("Doe", person.getSurname());
} }
@Test @Test
public void testAddMissingName() throws IOException { public void testAddMissingName() throws IOException {
final Form form = new Form(); final Form form = new Form();
form.param("surname", "Ximénez"); form.param("surname", newSurname());
final Response response = target("people") final Response response = target("people")
.request(MediaType.APPLICATION_JSON_TYPE) .request(MediaType.APPLICATION_JSON_TYPE)
.post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
assertBadRequestStatus(response); assertThat(response, hasBadRequestStatus());
} }
@Test @Test
public void testAddMissingSurname() throws IOException { public void testAddMissingSurname() throws IOException {
final Form form = new Form(); final Form form = new Form();
form.param("name", "Xoel"); form.param("name", newName());
final Response response = target("people") final Response response = target("people")
.request(MediaType.APPLICATION_JSON_TYPE) .request(MediaType.APPLICATION_JSON_TYPE)
.post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
assertBadRequestStatus(response); assertThat(response, hasBadRequestStatus());
} }
@Test @Test
@ExpectedDatabase("/datasets/dataset-modify.xml") @ExpectedDatabase("/datasets/dataset-modify.xml")
public void testModify() throws IOException { public void testModify() throws IOException {
final Form form = new Form(); final Form form = new Form();
form.param("name", "John"); form.param("name", newName());
form.param("surname", "Doe"); form.param("surname", newSurname());
final Response response = target("people/5") final Response response = target("people/" + existentId())
.request(MediaType.APPLICATION_JSON_TYPE) .request(MediaType.APPLICATION_JSON_TYPE)
.put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
assertOkStatus(response); assertThat(response, hasOkStatus());
final Person person = response.readEntity(Person.class); final Person modifiedPerson = response.readEntity(Person.class);
assertEquals(5, person.getId());
assertEquals("John", person.getName()); final Person person = existentPerson();
assertEquals("Doe", person.getSurname()); person.setName(newName());
person.setSurname(newSurname());
assertThat(modifiedPerson, is(equalsToPerson(person)));
} }
@Test @Test
public void testModifyName() throws IOException { public void testModifyName() throws IOException {
final Form form = new Form(); 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) .request(MediaType.APPLICATION_JSON_TYPE)
.put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
assertBadRequestStatus(response); assertThat(response, hasBadRequestStatus());
} }
@Test @Test
public void testModifySurname() throws IOException { public void testModifySurname() throws IOException {
final Form form = new Form(); 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) .request(MediaType.APPLICATION_JSON_TYPE)
.put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
assertBadRequestStatus(response); assertThat(response, hasBadRequestStatus());
} }
@Test @Test
public void testModifyInvalidId() throws IOException { public void testModifyInvalidId() throws IOException {
final Form form = new Form(); final Form form = new Form();
form.param("name", "Marta"); form.param("name", newName());
form.param("surname", "Méndez"); form.param("surname", newSurname());
final Response response = target("people/100") final Response response = target("people/" + nonExistentId())
.request(MediaType.APPLICATION_JSON_TYPE) .request(MediaType.APPLICATION_JSON_TYPE)
.put(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); .put(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
assertBadRequestStatus(response); assertThat(response, hasBadRequestStatus());
} }
@Test @Test
@ExpectedDatabase("/datasets/dataset-delete.xml") @ExpectedDatabase("/datasets/dataset-delete.xml")
public void testDelete() throws IOException { public void testDelete() throws IOException {
final Response response = target("people/4").request().delete(); final Response response = target("people/" + existentId()).request().delete();
assertOkStatus(response);
assertThat(response, hasOkStatus());
assertEquals(4, (int) response.readEntity(Integer.class)); final Integer deletedId = response.readEntity(Integer.class);
assertThat(deletedId, is(equalTo(existentId())));
} }
@Test @Test
public void testDeleteInvalidId() throws IOException { public void testDeleteInvalidId() throws IOException {
assertBadRequestStatus(target("people/100").request().delete()); final Response response = target("people/" + nonExistentId()).request().delete();
assertThat(response, hasBadRequestStatus());
} }
} }
package es.uvigo.esei.daa.rest; 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.anyInt;
import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.anyString; import static org.easymock.EasyMock.anyString;
...@@ -8,13 +20,13 @@ import static org.easymock.EasyMock.expect; ...@@ -8,13 +20,13 @@ import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify; import static org.easymock.EasyMock.verify;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.Arrays;
import java.util.List; import java.util.List;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
...@@ -45,77 +57,88 @@ public class PeopleResourceUnitTest { ...@@ -45,77 +57,88 @@ public class PeopleResourceUnitTest {
} }
@Test @Test
@SuppressWarnings("unchecked")
public void testList() throws Exception { public void testList() throws Exception {
final List<Person> people = Arrays.asList( final List<Person> people = asList(people());
new Person(1, "Pepe", "Pérez"),
new Person(2, "Paco", "Martínez"),
new Person(3, "Martina", "Juárez")
);
expect(daoMock.list()).andReturn(people); expect(daoMock.list()).andReturn(people);
replay(daoMock); replay(daoMock);
final Response response = resource.list(); final Response response = resource.list();
assertEquals(people, response.getEntity());
assertEquals(Status.OK, response.getStatusInfo()); assertThat(response, hasOkStatus());
assertThat((List<Person>) response.getEntity(), containsPeopleInAnyOrder(people()));
} }
@Test @Test
public void testListDAOException() throws Exception { public void testListDAOException() throws Exception {
expect(daoMock.list()).andThrow(new DAOException()); expect(daoMock.list()).andThrow(new DAOException());
replay(daoMock); replay(daoMock);
final Response response = resource.list(); final Response response = resource.list();
assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
assertThat(response, hasInternalServerErrorStatus());
} }
@Test @Test
public void testGet() throws Exception { 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); expect(daoMock.get(person.getId())).andReturn(person);
replay(daoMock); replay(daoMock);
final Response response = resource.get(person.getId()); 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 @Test
public void testGetDAOException() throws Exception { public void testGetDAOException() throws Exception {
expect(daoMock.get(anyInt())).andThrow(new DAOException()); expect(daoMock.get(anyInt())).andThrow(new DAOException());
replay(daoMock); replay(daoMock);
final Response response = resource.get(1); final Response response = resource.get(existentId());
assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
assertThat(response, hasInternalServerErrorStatus());
} }
@Test @Test
public void testGetIllegalArgumentException() throws Exception { public void testGetIllegalArgumentException() throws Exception {
expect(daoMock.get(anyInt())).andThrow(new IllegalArgumentException()); expect(daoMock.get(anyInt())).andThrow(new IllegalArgumentException());
replay(daoMock); replay(daoMock);
final Response response = resource.get(1); final Response response = resource.get(existentId());
assertEquals(Status.BAD_REQUEST, response.getStatusInfo());
assertThat(response, hasBadRequestStatus());
} }
@Test @Test
public void testDelete() throws Exception { public void testDelete() throws Exception {
daoMock.delete(anyInt()); daoMock.delete(anyInt());
replay(daoMock); replay(daoMock);
final Response response = resource.delete(1); final Response response = resource.delete(1);
assertEquals(Status.OK, response.getStatusInfo());
assertThat(response, hasOkStatus());
} }
@Test @Test
public void testDeleteDAOException() throws Exception { public void testDeleteDAOException() throws Exception {
daoMock.delete(anyInt()); daoMock.delete(anyInt());
expectLastCall().andThrow(new DAOException()); expectLastCall().andThrow(new DAOException());
replay(daoMock); replay(daoMock);
final Response response = resource.delete(1); final Response response = resource.delete(1);
assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
assertThat(response, hasInternalServerErrorStatus());
} }
@Test @Test
...@@ -125,12 +148,15 @@ public class PeopleResourceUnitTest { ...@@ -125,12 +148,15 @@ public class PeopleResourceUnitTest {
replay(daoMock); replay(daoMock);
final Response response = resource.delete(1); final Response response = resource.delete(1);
assertEquals(Status.BAD_REQUEST, response.getStatusInfo());
assertThat(response, hasBadRequestStatus());
} }
@Test @Test
public void testModify() throws Exception { 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); daoMock.modify(person);
...@@ -139,8 +165,8 @@ public class PeopleResourceUnitTest { ...@@ -139,8 +165,8 @@ public class PeopleResourceUnitTest {
final Response response = resource.modify( final Response response = resource.modify(
person.getId(), person.getName(), person.getSurname()); person.getId(), person.getName(), person.getSurname());
assertThat(response, hasOkStatus());
assertEquals(person, response.getEntity()); assertEquals(person, response.getEntity());
assertEquals(Status.OK, response.getStatusInfo());
} }
@Test @Test
...@@ -150,8 +176,9 @@ public class PeopleResourceUnitTest { ...@@ -150,8 +176,9 @@ public class PeopleResourceUnitTest {
replay(daoMock); replay(daoMock);
final Response response = resource.modify(1, "Paco", "Pérez"); final Response response = resource.modify(existentId(), newName(), newSurname());
assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
assertThat(response, hasInternalServerErrorStatus());
} }
@Test @Test
...@@ -160,24 +187,23 @@ public class PeopleResourceUnitTest { ...@@ -160,24 +187,23 @@ public class PeopleResourceUnitTest {
expectLastCall().andThrow(new IllegalArgumentException()); expectLastCall().andThrow(new IllegalArgumentException());
replay(daoMock); replay(daoMock);
final Response response = resource.modify(existentId(), newName(), newSurname());
final Response response = resource.modify(1, "Paco", "Pérez"); assertThat(response, hasBadRequestStatus());
assertEquals(Status.BAD_REQUEST, response.getStatusInfo());
} }
@Test @Test
public void testAdd() throws Exception { public void testAdd() throws Exception {
final Person person = new Person(1, "Pepe", "Pérez"); expect(daoMock.add(newName(), newSurname()))
.andReturn(newPerson());
expect(daoMock.add(person.getName(), person.getSurname()))
.andReturn(person);
replay(daoMock); replay(daoMock);
final Response response = resource.add( final Response response = resource.add(newName(), newSurname());
person.getName(), person.getSurname());
assertEquals(person, response.getEntity()); assertThat(response, hasOkStatus());
assertEquals(Status.OK, response.getStatusInfo()); assertThat((Person) response.getEntity(), is(equalsToPerson(newPerson())));
} }
@Test @Test
...@@ -186,8 +212,9 @@ public class PeopleResourceUnitTest { ...@@ -186,8 +212,9 @@ public class PeopleResourceUnitTest {
.andThrow(new DAOException()); .andThrow(new DAOException());
replay(daoMock); replay(daoMock);
final Response response = resource.add("Paco", "Pérez"); final Response response = resource.add(newName(), newSurname());
assertEquals(Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
assertThat(response, hasInternalServerErrorStatus());
} }
@Test @Test
...@@ -196,7 +223,8 @@ public class PeopleResourceUnitTest { ...@@ -196,7 +223,8 @@ public class PeopleResourceUnitTest {
.andThrow(new IllegalArgumentException()); .andThrow(new IllegalArgumentException());
replay(daoMock); replay(daoMock);
final Response response = resource.add("Paco", "Pérez"); final Response response = resource.add(newName(), newSurname());
assertEquals(Status.BAD_REQUEST, response.getStatusInfo());
assertThat(response, hasBadRequestStatus());
} }
} }
...@@ -5,9 +5,11 @@ import org.junit.runners.Suite; ...@@ -5,9 +5,11 @@ import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
import es.uvigo.esei.daa.dao.PeopleDAOUnitTest; import es.uvigo.esei.daa.dao.PeopleDAOUnitTest;
import es.uvigo.esei.daa.entities.PersonUnitTest;
import es.uvigo.esei.daa.rest.PeopleResourceUnitTest; import es.uvigo.esei.daa.rest.PeopleResourceUnitTest;
@SuiteClasses({ @SuiteClasses({
PersonUnitTest.class,
PeopleDAOUnitTest.class, PeopleDAOUnitTest.class,
PeopleResourceUnitTest.class PeopleResourceUnitTest.class
}) })
......
package es.uvigo.esei.daa; package es.uvigo.esei.daa.util;
import static org.junit.Assert.assertEquals;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.sql.DataSource; import javax.sql.DataSource;
import javax.ws.rs.core.Response;
import org.springframework.mock.jndi.SimpleNamingContextBuilder; import org.springframework.mock.jndi.SimpleNamingContextBuilder;
public final class TestUtils { public final class ContextUtils {
private final static SimpleNamingContextBuilder CONTEXT_BUILDER = private final static SimpleNamingContextBuilder CONTEXT_BUILDER =
new SimpleNamingContextBuilder(); new SimpleNamingContextBuilder();
private TestUtils() {} private ContextUtils() {}
public static void createFakeContext(DataSource datasource) public static void createFakeContext(DataSource datasource)
throws IllegalStateException, NamingException { throws IllegalStateException, NamingException {
...@@ -24,12 +21,4 @@ public final class TestUtils { ...@@ -24,12 +21,4 @@ public final class TestUtils {
CONTEXT_BUILDER.clear(); CONTEXT_BUILDER.clear();
CONTEXT_BUILDER.deactivate(); 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());
}
} }
package es.uvigo.esei.daa; package es.uvigo.esei.daa.util;
import static org.easymock.EasyMock.anyString; import static org.easymock.EasyMock.anyString;
import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createMock;
...@@ -18,6 +18,15 @@ import org.junit.Before; ...@@ -18,6 +18,15 @@ import org.junit.Before;
import com.mysql.jdbc.PreparedStatement; import com.mysql.jdbc.PreparedStatement;
/**
* Super-class for unit tests in the DAO layer.
*
* <p>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.</p>
*
* @author Miguel Reboiro Jato
*/
public abstract class DatabaseQueryUnitTest { public abstract class DatabaseQueryUnitTest {
protected DataSource datasource; protected DataSource datasource;
protected Connection connection; protected Connection connection;
...@@ -26,6 +35,11 @@ public abstract class DatabaseQueryUnitTest { ...@@ -26,6 +35,11 @@ public abstract class DatabaseQueryUnitTest {
protected boolean verify; protected boolean verify;
/**
* Configures the mocks and enables the verification.
*
* @throws Exception if an error happens while configuring the mocks.
*/
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
datasource = createMock(DataSource.class); datasource = createMock(DataSource.class);
...@@ -36,31 +50,46 @@ public abstract class DatabaseQueryUnitTest { ...@@ -36,31 +50,46 @@ public abstract class DatabaseQueryUnitTest {
expect(datasource.getConnection()) expect(datasource.getConnection())
.andReturn(connection); .andReturn(connection);
expect(connection.prepareStatement(anyString())) expect(connection.prepareStatement(anyString()))
.andReturn(statement); .andReturn(statement)
.anyTimes(); // statement is optional
expect(statement.executeQuery()) expect(statement.executeQuery())
.andReturn(result) .andReturn(result)
.anyTimes(); // executeQuery is optional; .anyTimes(); // executeQuery is optional
statement.close(); statement.close();
connection.close(); connection.close();
verify = true; verify = true;
} }
/**
* Removes the default behavior of the mock instances and disables the mock
* verification.
*/
protected void resetAll() { protected void resetAll() {
reset(result, statement, connection, datasource); reset(result, statement, connection, datasource);
verify = false; 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() protected void replayAll()
throws Exception { throws Exception {
replay(result, statement, connection, datasource); 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 @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
TestUtils.clearContextBuilder(); ContextUtils.clearContextBuilder();
try { try {
if (verify) { if (verify) {
......
package es.uvigo.esei.daa.web; 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.assertFalse;
import static org.junit.Assert.assertThat;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -78,42 +87,36 @@ public class PeopleWebTest { ...@@ -78,42 +87,36 @@ public class PeopleWebTest {
@Test @Test
public void testList() throws Exception { public void testList() throws Exception {
assertEquals(10, mainPage.countPeople()); assertThat(mainPage.listPeople(), containsPeopleInAnyOrder(people()));
} }
@Test @Test
@ExpectedDatabase("/datasets/dataset-add.xml") @ExpectedDatabase("/datasets/dataset-add.xml")
public void testAdd() throws Exception { public void testAdd() throws Exception {
final String name = "John"; final Person newPerson = mainPage.addPerson(newName(), newSurname());
final String surname = "Doe";
final Person newPerson = mainPage.addPerson(name, surname); assertThat(newPerson, is(equalsToPerson(newPerson())));
assertEquals(name, newPerson.getName());
assertEquals(surname, newPerson.getSurname());
} }
@Test @Test
@ExpectedDatabase("/datasets/dataset-modify.xml") @ExpectedDatabase("/datasets/dataset-modify.xml")
public void testEdit() throws Exception { public void testEdit() throws Exception {
final int id = 5; final Person person = existentPerson();
final String newName = "John"; person.setName(newName());
final String newSurname = "Doe"; 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()); assertThat(webPerson, is(equalsToPerson(person)));
assertEquals(newName, person.getName());
assertEquals(newSurname, person.getSurname());
} }
@Test @Test
@ExpectedDatabase("/datasets/dataset-delete.xml") @ExpectedDatabase("/datasets/dataset-delete.xml")
public void testDelete() throws Exception { public void testDelete() throws Exception {
mainPage.deletePerson(4); mainPage.deletePerson(existentId());
assertFalse(mainPage.hasPerson(4)); assertFalse(mainPage.hasPerson(existentId()));
} }
} }
package es.uvigo.esei.daa.web.pages; 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.presenceOfElementLocated;
import static org.openqa.selenium.support.ui.ExpectedConditions.textToBePresentInElement; import static org.openqa.selenium.support.ui.ExpectedConditions.textToBePresentInElement;
...@@ -42,6 +43,10 @@ public class MainPage { ...@@ -42,6 +43,10 @@ public class MainPage {
return new PeopleTable(this.driver).countPeople(); return new PeopleTable(this.driver).countPeople();
} }
public List<Person> listPeople() {
return new PeopleTable(this.driver).listPeople();
}
public Person getLastPerson() { public Person getLastPerson() {
return new PeopleTable(this.driver).getPersonInLastRow(); return new PeopleTable(this.driver).getPersonInLastRow();
} }
...@@ -66,13 +71,13 @@ public class MainPage { ...@@ -66,13 +71,13 @@ public class MainPage {
return table.getPerson(name, surname); 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); final PeopleTable table = new PeopleTable(this.driver);
table.editPerson(id); table.editPerson(person.getId());
final PersonForm form = new PersonForm(this.driver); final PersonForm form = new PersonForm(this.driver);
form.setName(newName); form.setName(person.getName());
form.setSurname(newSurname); form.setSurname(person.getSurname());
form.submit(); form.submit();
} }
...@@ -83,7 +88,6 @@ public class MainPage { ...@@ -83,7 +88,6 @@ public class MainPage {
} }
private final static class PeopleTable { private final static class PeopleTable {
private final WebDriver driver; private final WebDriver driver;
private final WebElement table; private final WebElement table;
...@@ -146,12 +150,19 @@ public class MainPage { ...@@ -146,12 +150,19 @@ public class MainPage {
} }
public int countPeople() { public int countPeople() {
return getRows().size();
}
public List<Person> listPeople() {
return getRows().stream()
.map(this::rowToPerson)
.collect(toList());
}
private List<WebElement> getRows() {
final String xpathQuery = "//tr[starts-with(@id, '" + ID_PREFIX + "')]"; final String xpathQuery = "//tr[starts-with(@id, '" + ID_PREFIX + "')]";
final List<WebElement> peopleRows = return this.table.findElements(By.xpath(xpathQuery));
this.table.findElements(By.xpath(xpathQuery));
return peopleRows.size();
} }
private Person rowToPerson(WebElement row) { private Person rowToPerson(WebElement row) {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" /> <property name="driverClassName" value="org.hsqldb.jdbc.JDBCDriver" />
<property name="url" value="jdbc:hsqldb:mem:daatestdb;create=true" /> <property name="url" value="jdbc:hsqldb:mem:daatestdb;create=true" />
<property name="username" value="daatestuser" /> <property name="username" value="daatestuser" />
<property name="password" value="daatestpass" /> <property name="password" value="daatestpass" />
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<people id="1" name="Antón" surname="Álvarez" /> <people id="1" name="Antón" surname="Álvarez" />
<people id="2" name="Ana" surname="Amargo" /> <people id="2" name="Ana" surname="Amargo" />
<people id="3" name="Manuel" surname="Martínez" /> <people id="3" name="Manuel" surname="Martínez" />
<people id="5" name="Lorenzo" surname="López" /> <people id="4" name="María" surname="Márquez" />
<people id="6" name="Laura" surname="Laredo" /> <people id="6" name="Laura" surname="Laredo" />
<people id="7" name="Perico" surname="Palotes" /> <people id="7" name="Perico" surname="Palotes" />
<people id="8" name="Patricia" surname="Pérez" /> <people id="8" name="Patricia" surname="Pérez" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment