------------------------------------------------------------------------------
Learn how Oracle Real Application Clusters (RAC) One Node allows customers
to consolidate database storage, standardize their database environment, and,
should the need arise, upgrade to a full multi-node Oracle RAC database
without downtime or disruption
http://p.sf.net/sfu/oracle-sfdevnl
I like:
* that it allows running tests in parallel
* that we avoid re-initializing test data over and over
To make it a 10:
* enforce data isolation. "Individual tests isolate their own data if
required" says otherwise, and I understand the reasons for pushing the
burden down to the actual acceptance tests. But I don't like it! This
got us into a heap of trouble in the integration tests (Udai spent a
Summer of Code decoupling them). Bugs caused by dependent/coupled data
can lay dormant for a long time and are complex to debug and fix.
Here are some other random ideas:
* Create a tool to help developers isolate test data. Let's call it
"checkClean". checkClean would make sure that each individual test
leaves the database exactly as it was before it started mucking about.
You might only run it on demand once a coupling/dependency is found
between tests.
* Split up the acceptance tests (ideally, dynamically) and have several
build slaves run them in parallel. For instance, say we have 100
acceptance tests. 1-20 are sent to slaveA, 21-40 are sent to slaveB,
41-60 are sent to slaveC, etc. A central job/server/script whatever
parcel out groups of tests and compiles test results. If this
parallelization is built into the TestNG/Selenium tests, we could simply
set parallel=true for the surefire plugin (and just turn this on for ci
builds).
I think by following the approach proposed by Vivek, we will make
acceptance tests simpler and faster.
The "Automated Acceptance Tests" should not be working like
"Functional Tests" (Service Facade level), i.e. running every
functional check in isolation.
The purpose of Acceptance tests should be just making sure that
application is working from the front end side (UI) for different work
flow.
These acceptance tests automate some of the work a QA would do
manually from the UI.
In Manual Acceptance testing, we usually set up one database (or two
for depends on different configuration) and then follow through the UI
to make sure everything is working as per expectation. Similarly for
Automated acceptance testing, it should not do too much at setUp() and
tearDown().
I like the isolation of test data by target objects (different
customers and accounts).
I remember during work on Integration Tests when we were not able to
rollback because of the commits being called from main code, we
isolated tests by changing the target object names like
createClient("Client_TestName) in setup methods, while previously it
was createClient("Cleint") in all the tests causing collision (or
duplicate customer error) in case of failure by one tests.
Acceptance tests won't rollback hence isolation by target object name
should work.
to make it 10, I will still keep the dump in DbUnit format, because I
don't want us to hardcode MySQL in Mifos wherever it can be avoided,
because later it will become a blocker if we decide to try out other
databases.
Cheers,
Udai
------------------------------------------------------------------------------
Gaining the trust of online customers is vital for the success of any company
that requires sensitive data to be transmitted over the Web. Learn how to
best implement a security strategy that keeps consumers' information secure
and instills the confidence they need to proceed with transactions.
http://p.sf.net/sfu/oracle-sfdevnl
------------------------------------------------------------------------------
Gaining the trust of online customers is vital for the success of any company
that requires sensitive data to be transmitted over the Web. Learn how to
best implement a security strategy that keeps consumers' information secure
and instills the confidence they need to proceed with transactions.
http://p.sf.net/sfu/oracle-sfdevnl
package data; import com.google.common.collect.Sets; import org.dbunit.dataset.*; import org.mifos.framework.util.DbUnitUtilities; import java.io.File; import java.io.IOException; import java.util.*; public class AcceptanceDataSets { private static UniqueKeysMap uniqueKeysMap = new UniqueKeysMap(); public static void main(String[] args) throws Exception { File directory = new File("acceptanceTests/src/test/resources/dataSets"); File[] files = directory.listFiles(); DbUnitUtilities dbUnitUtilities = new DbUnitUtilities(); ITestDatabase testDatabase = TestDatabase.connect("root", "", "mifos"); Set<Row> union = new HashSet<Row>(); RowsForExactMatch rejectedRows = new RowsForExactMatch(); IgnoredDataSets ignoredDataSets = new IgnoredDataSets(); for (File file : files) { if (file.getName().endsWith(".xml") && !ignoredDataSets.isIgnored(file.getName())) { System.out.println("Constructing dataSet for " + file.getName()); IDataSet dbUnitDataSet = dbUnitUtilities.getDataSetFromFile(file.getAbsolutePath()); String[] tables = dbUnitDataSet.getTableNames(); Set<Row> rows = getAllRows(testDatabase, dbUnitDataSet, tables); System.out.println("Number of items in the current data set: " + rows.size()); rejectedRows.addAll(Sets.intersection(rows, union), file.getName()); union = Sets.union(rows, union); System.out.println("Number of items in the union data set: " + union.size()); } } writeAllTablesToFile(union); writeAllRejectedToFile(rejectedRows, union); } private static void writeAllRejectedToFile(RowsForExactMatch rejectedRows, Set<Row> selectedRows) throws IOException { RowsForExactMatch selectedRowsForExactMatch = new RowsForExactMatch(); selectedRowsForExactMatch.addAll(selectedRows, ""); ArrayList<RowForExactMatch> nonDuplicateRejectedRows = new ArrayList<RowForExactMatch>(Sets.difference(rejectedRows, selectedRowsForExactMatch)); Collections.sort(nonDuplicateRejectedRows, new Comparator<RowForExactMatch>() { @Override public int compare(RowForExactMatch row1, RowForExactMatch row2) { return row1.getTag().compareTo(row2.getTag()); } }); SimpleFile simpleFile = new SimpleFile("/home/vivek/projects/mifos/rejected.sql"); String lastTag = null; for (RowForExactMatch rowForExactMatch : nonDuplicateRejectedRows) { if (!rowForExactMatch.getTag().equals(lastTag)) { lastTag = rowForExactMatch.getTag(); simpleFile.writeLine(String.format("FILE %s;", lastTag)); simpleFile.writeLine(); } simpleFile.writeLine(rowForExactMatch.toInsertSql()); } simpleFile.close(); } private static Set<Row> getAllRows(ITestDatabase testDatabase, IDataSet dbUnitDataSet, String[] tables) throws DataSetException { Set<Row> rows = new HashSet<Row>(); for (String tableName : tables) { ITable dbUnitTable = dbUnitDataSet.getTable(tableName); Table table = new Table(tableName, testDatabase, uniqueKeysMap.get(tableName)); ITableMetaData tableMetaData = dbUnitTable.getTableMetaData(); Column[] columns = tableMetaData.getColumns(); int rowCount = dbUnitTable.getRowCount(); for (int i = 0; i < rowCount; i++) { Row row = new Row(table); for (Column column : columns) { Object value = dbUnitTable.getValue(i, column.getColumnName()); row.setColumnValue(column.getColumnName(), value); } rows.add(row); } } return rows; } private static ArrayList<String> writeAllTablesToFile(Set<Row> union) throws IOException { ArrayList<Row> result = new ArrayList<Row>(union); Collections.sort(result, new Comparator<Row>() { @Override public int compare(Row row1, Row row2) { return row1.getTableName().compareTo(row2.getTableName()); } }); IgnoredTables ignoredTables = new IgnoredTables(); int numberOfRowsInATable = 0; ArrayList<String> allTables = new ArrayList<String>(); String outputFile = "/home/vivek/projects/mifos/functional-test-data.sql"; System.out.println("Writing to file " + outputFile); SimpleFile simpleFile = new SimpleFile(outputFile); try { simpleFile.writeLine("SET FOREIGN_KEY_CHECKS=0;"); String lastTableName = ""; for (Row row : result) { String tableName = row.getTable().getName(); if (ignoredTables.contains(tableName)) continue; if (!tableName.equals(lastTableName)) { System.out.printf("%s: %d%n", lastTableName, numberOfRowsInATable); numberOfRowsInATable = 0; lastTableName = row.getTable().getName(); allTables.add(lastTableName); simpleFile.writeLine(String.format("select \"TABLE: %s\";", lastTableName)); simpleFile.writeLine(String.format("delete from %s;", lastTableName)); simpleFile.writeLine("commit;"); } simpleFile.writeLine(row.toInsertSql()); numberOfRowsInATable++; } simpleFile.writeLine("SET FOREIGN_KEY_CHECKS=1;"); } finally { simpleFile.close(); } return allTables; } }package data; import org.apache.commons.lang.StringUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; public class Row { private Table table; private HashMap<String, Object> data = new HashMap<String, Object>(); public Row(Table table) { this.table = table; } public void setColumnValue(String columnName, Object value) { data.put(columnName, value); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Row row = (Row) o; if (!table.equals(row.table)) return false; boolean primaryKeyMatched = true; PrimaryKey primaryKey = table.getPrimaryKey(); ArrayList<String> primaryKeyColumns = primaryKey.getColumns(); if (primaryKeyColumns.size() != 0) { for (String column : primaryKeyColumns) primaryKeyMatched &= (columnValueEquals(row, column)); if (primaryKeyMatched) return true; } if (table.getUniqueKeyColumn() != null && columnValueEquals(row, table.getUniqueKeyColumn())) return true; if (primaryKeyColumns.size() == 0) return data.equals(row.data); return primaryKeyMatched; } private boolean columnValueEquals(Row row, String column) { return data.get(column).equals(row.data.get(column)); } @Override public int hashCode() { return table.hashCode(); } @Override public String toString() { return String.format("table=%s, %s", table, data); } public Table getTable() { return table; } public String toInsertSql() { return toInsertSql(table.getName(), data); } public static String toInsertSql(String tableName, HashMap<String, Object> data) { StringBuffer buffer = new StringBuffer(); buffer.append("insert into ").append(tableName).append(" ("); buffer.append(StringUtils.join(data.keySet().toArray(), ',')); buffer.append(") values ("); Iterator<String> iterator = data.keySet().iterator(); while (iterator.hasNext()) { Object columnValue = data.get(iterator.next()); if (columnValue == null) buffer.append("null"); else buffer.append("'").append(columnValue.toString().replaceAll("'", "''")).append("'"); if (iterator.hasNext()) buffer.append(","); } buffer.append(");"); return buffer.toString(); } public RowForExactMatch cloneForExactMatch(String tag) { return new RowForExactMatch(table.getName(), data, tag); } public String getTableName() { return table.getName(); } public String primaryKeySqlCondition() { StringBuffer stringBuffer = new StringBuffer(); PrimaryKey key = table.getPrimaryKey(); for (String column : key.getColumns()) { stringBuffer.append(column).append("='").append(data.get(column)).append("'"); } return stringBuffer.toString(); } }package data; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; @Test public class RowTest { private HashMap map; private Table table; private Row row; private StubTestDatabase testDatabase; @BeforeMethod public void setup() { map = new HashMap(); map.put("foo", new PrimaryKey("abc")); map.put("bar", new PrimaryKey()); testDatabase = new StubTestDatabase(map); table = new Table("foo", testDatabase, null); row = new Row(table); } public void noPrimaryKeyAndUnequalUniqueKeyShouldBeUnequal() { Table tableWithNoPrimaryKey = new Table("bar", testDatabase, "abc"); Row row1 = new Row(tableWithNoPrimaryKey); row1.setColumnValue("abc", 123); row1.setColumnValue("efg", 456); Row row2 = new Row(tableWithNoPrimaryKey); row2.setColumnValue("abc", 321); row2.setColumnValue("efg", 456); Assert.assertFalse(row1.equals(row2)); } public void sameIsEqual() { row.setColumnValue("abc", 123); row.setColumnValue("efg", 456); Assert.assertEquals(row, row); Assert.assertEquals(row.hashCode(), row.hashCode()); } public void equalWhenColumnOrderIsDifferent() { Row row1 = new Row(table); row1.setColumnValue("abc", 123); row1.setColumnValue("efg", 456); Row row2 = new Row(table); row2.setColumnValue("efg", 456); row2.setColumnValue("abc", 123); Assert.assertTrue(row1.equals(row2)); Assert.assertTrue(row1.hashCode() == row2.hashCode()); } public void rowFromDifferentTableAreNotEqual() { Row row1 = new Row(table); row1.setColumnValue("abc", 123); row1.setColumnValue("efg", 456); Table otherTable = new Table("bar", testDatabase, null); Row row2 = new Row(otherTable); row2.setColumnValue("abc", 123); row2.setColumnValue("efg", 456); Assert.assertFalse(row1.equals(row2)); Assert.assertFalse(row1.hashCode() == row2.hashCode()); } public void rowWithDifferentPrimaryKeyAreNotEqual() { Row row1 = new Row(table); row1.setColumnValue("abc", 124); row1.setColumnValue("efg", 456); Row row2 = new Row(table); row2.setColumnValue("abc", 123); row2.setColumnValue("efg", 456); Assert.assertFalse(row1.equals(row2)); } public void rowWithSamePrimaryKeyButDifferentDataAreEqual() { Row row1 = new Row(table); row1.setColumnValue("abc", 123); row1.setColumnValue("efg", 456); Row row2 = new Row(table); row2.setColumnValue("abc", 123); row2.setColumnValue("efg", 457); Assert.assertTrue(row1.equals(row2)); Assert.assertTrue(row1.hashCode() == row2.hashCode()); } public void toInsertSql() { row.setColumnValue("abc", 123); row.setColumnValue("efg", 456); String expectedSql = "insert into foo (abc,efg) values ('123','456');"; Assert.assertEquals(expectedSql, row.toInsertSql()); } public void toInsertSqlWhenTheColumnValueIsNull() { row.setColumnValue("abc", 123); row.setColumnValue("efg", null); String expectedSql = "insert into foo (abc,efg) values ('123',null);"; Assert.assertEquals(expectedSql, row.toInsertSql()); } public void primaryKeySqlCondition() { row.setColumnValue("abc", 123); row.setColumnValue("efg", 456); Assert.assertEquals("abc='123'", row.primaryKeySqlCondition()); } public void rowsEqualWhenNoPrimaryKeyPresent() { Table otherTable = new Table("bar", testDatabase, null); Row row1 = new Row(otherTable); row1.setColumnValue("abc", 123); Row row2 = new Row(otherTable); row2.setColumnValue("abc", 123); Assert.assertTrue(row1.equals(row2)); Assert.assertTrue(row1.hashCode() == row2.hashCode()); } public void rowsUnEqualWhenNoPrimaryKeyPresent() { Table otherTable = new Table("bar", testDatabase, null); Row row1 = new Row(otherTable); row1.setColumnValue("abc", 123); Row row2 = new Row(otherTable); row2.setColumnValue("abc", 124); Assert.assertFalse(row1.equals(row2)); } }package data; import com.google.common.collect.Sets; import org.testng.Assert; import org.testng.annotations.Test; import java.util.HashMap; import java.util.HashSet; import java.util.Set; @Test public class SetsTest { private HashMap map; private Table table; private Row row; private StubTestDatabase testDatabase; public void uniqueKeyRepeated() { map = new HashMap(); map.put("foo", new PrimaryKey("abc")); testDatabase = new StubTestDatabase(map); table = new Table("foo", testDatabase, "efg"); Row row = new Row(table); row.setColumnValue("abc", "123"); row.setColumnValue("efg", "456"); Set<Row> firstSet = new HashSet<Row>(); firstSet.add(row); row = new Row(table); row.setColumnValue("abc", "321"); row.setColumnValue("efg", "456"); Set<Row> secondSet = new HashSet<Row>(); secondSet.add(row); Assert.assertEquals(1, Sets.intersection(firstSet, secondSet).size()); Assert.assertEquals(1, Sets.union(firstSet, secondSet).size()); } public void primaryKeyRepeated() { map = new HashMap(); map.put("foo", new PrimaryKey("abc")); testDatabase = new StubTestDatabase(map); table = new Table("foo", testDatabase, "efg"); Row row = new Row(table); row.setColumnValue("abc", "123"); row.setColumnValue("efg", "654"); Set<Row> firstSet = new HashSet<Row>(); firstSet.add(row); row = new Row(table); row.setColumnValue("abc", "123"); row.setColumnValue("efg", "456"); Set<Row> secondSet = new HashSet<Row>(); secondSet.add(row); Assert.assertEquals(1, Sets.intersection(firstSet, secondSet).size()); Assert.assertEquals(1, Sets.union(firstSet, secondSet).size()); } public void noPrimaryKeyWithUniqueKey() { map = new HashMap(); testDatabase = new StubTestDatabase(map); table = new Table("foo", testDatabase, "efg"); Row row = new Row(table); row.setColumnValue("abc", "123"); row.setColumnValue("efg", "654"); Set<Row> firstSet = new HashSet<Row>(); firstSet.add(row); row = new Row(table); row.setColumnValue("abc", "123"); row.setColumnValue("efg", "456"); Set<Row> secondSet = new HashSet<Row>(); secondSet.add(row); Assert.assertEquals(0, Sets.intersection(firstSet, secondSet).size()); Assert.assertEquals(2, Sets.union(firstSet, secondSet).size()); } public void noPrimaryKeyWithUniqueKeyAndSomeColumnsNull() { map = new HashMap(); testDatabase = new StubTestDatabase(map); table = new Table("foo", testDatabase, "efg"); Row row = new Row(table); row.setColumnValue("abc", null); row.setColumnValue("efg", "654"); Set<Row> firstSet = new HashSet<Row>(); firstSet.add(row); row = new Row(table); row.setColumnValue("abc", "123"); row.setColumnValue("efg", "456"); Set<Row> secondSet = new HashSet<Row>(); secondSet.add(row); Assert.assertEquals(0, Sets.intersection(firstSet, secondSet).size()); Assert.assertEquals(2, Sets.union(firstSet, secondSet).size()); } }
------------------------------------------------------------------------------
Protect Your Site and Customers from Malware Attacks
Learn about various malware tactics and how to avoid them. Understand
malware threats, the impact they can have on your business, and how you
can protect your company and customers by using code signing.
http://p.sf.net/sfu/oracle-sfdevnl