I've written two matchers for my own use that others might find useful.
The first is a SignificantDigitMatcher that only cares about floating point values up to a certain number of digits. This is especially useful given the variability in how floats are displayed, especially if you're using copy-paste from a GUI to generate test scripts.
The second is a ForgivingNullMatcher that is, well, 'forgiving' about the way you represent NULLs in your test script. Again especially useful if you're using copy-paste from a GUI to generate test scripts.
I'll try to copy the code inline below, but will also email them to the maintainers:
/*
* $Id: ForgivingNullMatcher.java,v 1.0 2005/06/14 19:15:45 spal Exp $
* $Source: /cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/matchers/ForgivingNullMatcher.java,v $
* SQLUnit - a test harness for unit testing database stored procedures.
* Copyright (C) 2003 The SQLUnit Team
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
* The ForgivingNullMatcher is an implementation of the IMatcher interface
* used to compare values you know are either supposed to be NULL (but might be textually
* represented differently) or are actually equal. Basically searches for
* the word NULL (case insensitive) or for blank elements or for elements that are
* equal Strings. Especially useful when you're generating your test script
* by copying and pasting out of a SQL query tool that displays NULLs differently
* than SQLUnit pulls back from the database.
*
* For example, all of these values are treated as equal:
*
* <col id="14" name="proxy_for_benchcode" type="INTEGER">[NULL]</col>
* <col id="14" name="proxy_for_benchcode" type="INTEGER">NULL</col>
* <col id="14" name="proxy_for_benchcode" type="INTEGER"></col>
* <col id="14" name="proxy_for_benchcode" type="INTEGER">null</col>
*
* Example configuration:
* <match resultset-id="1" row-id="*" col-id="14"
matcher="net.sourceforge.sqlunit.matchers.ForgivingNullMatcher">
</match>
* @author Tim Cull (trcull@yahoo.com)
* @version $Revision: 1.0 $
*/
public class ForgivingNullMatcher implements IMatcher {
public static final String NULL_STRING = "NULL";
/**
*
*/
public ForgivingNullMatcher() {
super();
}
/**
* Returns true if the value of the target and the source
* are equivalent to NULL (either by being empty or by containing the word NULL)
* or are actually equal to each other
* @param source the String representing the source to be matched.
* @param target the String representing the target to be matched.
* @param args a Map of name value pairs of arguments passed in.
* @return true if the matching strategy resulted in success.
* @exception SQLUnitException if there was a problem with matching.
*/
public boolean isEqual(String source, String target, Map args)
throws SQLUnitException {
int iSourceLength = source.length();
int iTargetLength = target.length();
boolean retVal = false;
if (iSourceLength==0 && iTargetLength==0){//both are empty
retVal = true;
} else if (iSourceLength==0){//only source is empty
target = target.toUpperCase();//eliminate effect of case
if(target.indexOf(NULL_STRING)>-1){
retVal = true;
}
} else if (iTargetLength==0){//only target is empty
source = source.toUpperCase();//eliminate effect of case
if(source.indexOf(NULL_STRING)>-1){
retVal = true;
}
} else {//neither is empty
if (source.equals(target)){ //either they're both actually equal, or they're both the same textual representation of NULL
retVal = true;
} else { //they're not exactly equal, but might still be different textual representations of NULL
source = source.toUpperCase();
target = target.toUpperCase();
if(target.indexOf(NULL_STRING)>-1 && source.indexOf(NULL_STRING)>-1){
retVal = true;
}
}
}
return retVal;
}
}
/*
* $Id: SignificantDigitsMatcher.java,v 1.0 2005/06/14 19:15:45 spal Exp $
* $Source: /cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/matchers/SignificantDigitsMatcher.java,v $
* SQLUnit - a test harness for unit testing database stored procedures.
* Copyright (C) 2003 The SQLUnit Team
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.sourceforge.sqlunit.matchers;
/**
* The SignificantDigitsMatcher is an implementation of the IMatcher interface
* used to compare FLOAT values where you only really care about matching to a certain
* number of significant digits to the right of the decimal. Especially useful when
* you're generating your test script by copying and pasting out of a SQL query tool
* that displays a different number of significant
* digits than SQLUnit pulls back from the database.
*
* Example configuration:
* <match resultset-id="1" row-id="*" col-id="9-13,15-18"
matcher="net.sourceforge.sqlunit.matchers.SignificantDigitsMatcher">
<arg name="signif-digits" value="3" />
</match>
* @sqlunit.matcher.arg name="signif-digits"
* description="Number of significant digits to match to"
* @author Tim Cull (trcull@yahoo.com)
* @version $Revision: 1.0 $
*/
public class SignificantDigitsMatcher implements IMatcher {
/**
*
*/
public SignificantDigitsMatcher() {
super();
}
/**
* Returns true if the value of the target is equal to the source
* up to a certain number of significant digits.
* @param source the String representing the source to be matched.
* @param target the String representing the target to be matched.
* @param args a Map of name value pairs of arguments passed in.
* @return true if the matching strategy resulted in success.
* @exception SQLUnitException if there was a problem with matching.
*/
public boolean isEqual(String source, String target, Map args)
throws SQLUnitException {
String aDigits = (String) args.get("signif-digits");
if (aDigits == null) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value for key 'signif-digits' is NULL"});
}
// is digits an integer?
int iDigits = 0;
try {
iDigits = Integer.parseInt(aDigits);
} catch (NumberFormatException e) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value of key 'signif-digits' is not an INT"});
}
// cannot have the digits less than 0
if (iDigits < 0) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value of key 'signif-digits' must be greater than or equal to zero"});
}
// is the source a float?
BigDecimal dSource;
try {
dSource = new BigDecimal(source);
} catch (NumberFormatException e) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value of 'source' is not a FLOAT"});
}
// is the target a float?
BigDecimal dTarget;
try {
dTarget = new BigDecimal(target);
} catch (NumberFormatException e) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value of 'target' is not a FLOAT"});
}
//convert source to a BigDecimal with right number of decimal places
dSource = dSource.setScale(iDigits,BigDecimal.ROUND_HALF_UP );
//convert target to a BigDecimal with right number of decimal places
I've written two matchers for my own use that others might find useful.
The first is a SignificantDigitMatcher that only cares about floating point values up to a certain number of digits. This is especially useful given the variability in how floats are displayed, especially if you're using copy-paste from a GUI to generate test scripts.
The second is a ForgivingNullMatcher that is, well, 'forgiving' about the way you represent NULLs in your test script. Again especially useful if you're using copy-paste from a GUI to generate test scripts.
I'll try to copy the code inline below, but will also email them to the maintainers:
/*
* $Id: ForgivingNullMatcher.java,v 1.0 2005/06/14 19:15:45 spal Exp $
* $Source: /cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/matchers/ForgivingNullMatcher.java,v $
* SQLUnit - a test harness for unit testing database stored procedures.
* Copyright (C) 2003 The SQLUnit Team
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.sourceforge.sqlunit.matchers;
import java.util.Map;
import net.sourceforge.sqlunit.IMatcher;
import net.sourceforge.sqlunit.SQLUnitException;
/**
* The ForgivingNullMatcher is an implementation of the IMatcher interface
* used to compare values you know are either supposed to be NULL (but might be textually
* represented differently) or are actually equal. Basically searches for
* the word NULL (case insensitive) or for blank elements or for elements that are
* equal Strings. Especially useful when you're generating your test script
* by copying and pasting out of a SQL query tool that displays NULLs differently
* than SQLUnit pulls back from the database.
*
* For example, all of these values are treated as equal:
*
* <col id="14" name="proxy_for_benchcode" type="INTEGER">[NULL]</col>
* <col id="14" name="proxy_for_benchcode" type="INTEGER">NULL</col>
* <col id="14" name="proxy_for_benchcode" type="INTEGER"></col>
* <col id="14" name="proxy_for_benchcode" type="INTEGER">null</col>
*
* Example configuration:
* <match resultset-id="1" row-id="*" col-id="14"
matcher="net.sourceforge.sqlunit.matchers.ForgivingNullMatcher">
</match>
* @author Tim Cull (trcull@yahoo.com)
* @version $Revision: 1.0 $
*/
public class ForgivingNullMatcher implements IMatcher {
public static final String NULL_STRING = "NULL";
/**
*
*/
public ForgivingNullMatcher() {
super();
}
/**
* Returns true if the value of the target and the source
* are equivalent to NULL (either by being empty or by containing the word NULL)
* or are actually equal to each other
* @param source the String representing the source to be matched.
* @param target the String representing the target to be matched.
* @param args a Map of name value pairs of arguments passed in.
* @return true if the matching strategy resulted in success.
* @exception SQLUnitException if there was a problem with matching.
*/
public boolean isEqual(String source, String target, Map args)
throws SQLUnitException {
int iSourceLength = source.length();
int iTargetLength = target.length();
boolean retVal = false;
if (iSourceLength==0 && iTargetLength==0){//both are empty
retVal = true;
} else if (iSourceLength==0){//only source is empty
target = target.toUpperCase();//eliminate effect of case
if(target.indexOf(NULL_STRING)>-1){
retVal = true;
}
} else if (iTargetLength==0){//only target is empty
source = source.toUpperCase();//eliminate effect of case
if(source.indexOf(NULL_STRING)>-1){
retVal = true;
}
} else {//neither is empty
if (source.equals(target)){ //either they're both actually equal, or they're both the same textual representation of NULL
retVal = true;
} else { //they're not exactly equal, but might still be different textual representations of NULL
source = source.toUpperCase();
target = target.toUpperCase();
if(target.indexOf(NULL_STRING)>-1 && source.indexOf(NULL_STRING)>-1){
retVal = true;
}
}
}
return retVal;
}
}
/*
* $Id: SignificantDigitsMatcher.java,v 1.0 2005/06/14 19:15:45 spal Exp $
* $Source: /cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/matchers/SignificantDigitsMatcher.java,v $
* SQLUnit - a test harness for unit testing database stored procedures.
* Copyright (C) 2003 The SQLUnit Team
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.sourceforge.sqlunit.matchers;
import java.math.BigDecimal;
import java.util.Map;
import net.sourceforge.sqlunit.IErrorCodes;
import net.sourceforge.sqlunit.IMatcher;
import net.sourceforge.sqlunit.SQLUnitException;
/**
* The SignificantDigitsMatcher is an implementation of the IMatcher interface
* used to compare FLOAT values where you only really care about matching to a certain
* number of significant digits to the right of the decimal. Especially useful when
* you're generating your test script by copying and pasting out of a SQL query tool
* that displays a different number of significant
* digits than SQLUnit pulls back from the database.
*
* Example configuration:
* <match resultset-id="1" row-id="*" col-id="9-13,15-18"
matcher="net.sourceforge.sqlunit.matchers.SignificantDigitsMatcher">
<arg name="signif-digits" value="3" />
</match>
* @sqlunit.matcher.arg name="signif-digits"
* description="Number of significant digits to match to"
* @author Tim Cull (trcull@yahoo.com)
* @version $Revision: 1.0 $
*/
public class SignificantDigitsMatcher implements IMatcher {
/**
*
*/
public SignificantDigitsMatcher() {
super();
}
/**
* Returns true if the value of the target is equal to the source
* up to a certain number of significant digits.
* @param source the String representing the source to be matched.
* @param target the String representing the target to be matched.
* @param args a Map of name value pairs of arguments passed in.
* @return true if the matching strategy resulted in success.
* @exception SQLUnitException if there was a problem with matching.
*/
public boolean isEqual(String source, String target, Map args)
throws SQLUnitException {
String aDigits = (String) args.get("signif-digits");
if (aDigits == null) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value for key 'signif-digits' is NULL"});
}
// is digits an integer?
int iDigits = 0;
try {
iDigits = Integer.parseInt(aDigits);
} catch (NumberFormatException e) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value of key 'signif-digits' is not an INT"});
}
// cannot have the digits less than 0
if (iDigits < 0) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value of key 'signif-digits' must be greater than or equal to zero"});
}
// is the source a float?
BigDecimal dSource;
try {
dSource = new BigDecimal(source);
} catch (NumberFormatException e) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value of 'source' is not a FLOAT"});
}
// is the target a float?
BigDecimal dTarget;
try {
dTarget = new BigDecimal(target);
} catch (NumberFormatException e) {
throw new SQLUnitException(IErrorCodes.MATCHER_EXCEPTION,
new String[] {this.getClass().getName(),
"Value of 'target' is not a FLOAT"});
}
//convert source to a BigDecimal with right number of decimal places
dSource = dSource.setScale(iDigits,BigDecimal.ROUND_HALF_UP );
//convert target to a BigDecimal with right number of decimal places
dTarget = dTarget.setScale(iDigits,BigDecimal.ROUND_HALF_UP);
System.out.println("comparing " + dTarget + "("+target+") and " + dSource + "("+source+"): "+dSource.equals(dTarget));
// return the match
return dSource.equals(dTarget);
}
}
ps. SourceForge took all the indentation out of my code. I promise I don't write code that ugly in reality.
Hi Tim,
> I promise I don't write code that ugly in reality.
:-)
Thanks again for your contributions and they are in CVS now. Heres the commit log.
RCS file: /cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/matchers/ForgivingNullMatcher.java,v
done
Checking in src/net/sourceforge/sqlunit/matchers/ForgivingNullMatcher.java;
/cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/matchers/ForgivingNullMatcher.java,v <-- ForgivingNullMatcher.java
initial revision: 1.1
done
RCS file: /cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/matchers/SignificantDigitsMatcher.java,v
done
Checking in src/net/sourceforge/sqlunit/matchers/SignificantDigitsMatcher.java;
/cvsroot/sqlunit/sqlunit/src/net/sourceforge/sqlunit/matchers/SignificantDigitsMatcher.java,v <-- SignificantDigitsMatcher.java
initial revision: 1.1
done
-sujit