I'm doing some maintenance on an older web application written in Monorail v1.0.3. I want to unit test an action that uses RenderText(). How do I extract the content in my test? Reading from controller.Response.OutputStream doesn't work, since the response stream is either not setup properly in PrepareController(), or is closed in RenderText().
Example Action
public DeleteFoo( int id )
{
var success= false;
var foo = Service.Get<Foo>( id );
if( foo != null && CurrentUser.IsInRole( "CanDeleteFoo" ) )
{
Service.Delete<Foo>( id );
success = true;
}
CancelView();
RenderText( "{ success: " + success + " }" );
}
Example Test (using Moq)
[Test]
public void DeleteFoo()
{
var controller = new FooController ();
PrepareController ( controller );
var foo = new Foo { Id = 123 };
var mockService = new Mock < Service > ();
mockService.Setup ( s => s.Get<Foo> ( foo.Id ) ).Returns ( foo );
controller.Service = mockService.Object;
controller.DeleteTicket ( foo.Id );
mockService.Verify ( s => s.Delete<Foo> ( foo.Id ) );
Assert.AreEqual ( "{success:true}", GetResponse ( Response ) );
}
// response.OutputStream.Seek throws an "System.ObjectDisposedException: Cannot access a closed Stream." exception
private static string GetResponse( IResponse response )
{
response.OutputStream.Seek ( 0, SeekOrigin.Begin );
var buffer = new byte[response.OutputStream.Length];
response.OutputStream.Read ( buffer, 0, buffer.Length );
return Encoding.ASCII.GetString ( buffer );
}
Override BaseControllerTest.BuildResponse() and provide your mock of IMockResponse built with Moq.
Related
How to associate Kentico changes from REST API or External Application (like windows console application) to a staging task group
static void Main ( string [ ] args )
{
UserInfo userInfo = UserInfoProvider.GetUserInfo ( "User1" );
using ( new CMSActionContext ( userInfo ) )
{
TreeProvider treeProvider = new TreeProvider ( userInfo );
NodeSelectionParameters nodeSelectionParameters = new NodeSelectionParameters
{
AliasPath = "Path"
};
TreeNode parentPage = treeProvider.SelectSingleNode ( nodeSelectionParameters );
TreeNode newPage = TreeNode.New ( "Class", treeProvider );
newPage.DocumentName = "Test Title";
newPage.DocumentCulture = "en-us";
newPage.DocumentUrlPath = "Path";
newPage.Insert ( parentPage );
}
}
The above code is properly creating the document and the staging tasks with the user context. How can I associate the staging tasks for this document to a staging task group?
First I'd look to make sure that the objects that you are changing are in the list of items supported by content staging. you can see that list here: Content staging - What can be synchronized
So long as you are ultimately using Kentico's API, and Content Staging is enabled, then Kentico should be creating these tasks for you. If you're updating the Kentico database directly without the API, you're probably going to run into trouble and might need to manually create staging tasks or use the API to perform synchronisation.
We can use the SynchronizationActionContext class to associate API changes to a staging task group.
static void Main ( string [ ] args )
{
List<TaskGroupInfo> taskGroups = TaskGroupInfoProvider.GetTaskGroups ( ).WhereEquals ( "TaskGroupCodeName", "MyTaskGroup" ).ToList ( );
using ( new SynchronizationActionContext ( ) { TaskGroups = taskGroups } )
{
UserInfo userInfo = UserInfoProvider.GetUserInfo ( "User1" );
using ( new CMSActionContext ( userInfo ) )
{
TreeProvider treeProvider = new TreeProvider ( userInfo );
NodeSelectionParameters nodeSelectionParameters = new NodeSelectionParameters
{
AliasPath = "Path"
};
TreeNode parentPage = treeProvider.SelectSingleNode ( nodeSelectionParameters );
TreeNode newPage = TreeNode.New ( "Class", treeProvider );
newPage.DocumentName = "Test Title";
newPage.DocumentCulture = "en-us";
newPage.DocumentUrlPath = "Path";
newPage.Insert ( parentPage );
}
}
}
Is there a way to configure Tomcat 7 to create JSESSIONID cookie with a secure flag in all occasions?
Usual configuration results in Tomcat flagging session cookie with secure flag only if connection is made through https. However in my production scenario, Tomcat is behind a reverse proxy/load balancer which handles (and terminates) the https connection and contacts tomcat over http.
Can I somehow force secure flag on session cookie with Tomcat, even though connection is made through plain http?
In the end, contrary to my initial tests, web.xml solution worked for me on Tomcat 7.
E.g. I added this snippet to web.xml and it marks session cookie as secure even when reverse proxy contacts tomcat over plain HTTP.
<session-config>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>
ServletContext.getSessionCookieConfig().setSecure(true)
Another approach, similar to Mark's, would be to use the SessionCookieConfig, but set it in a context listener from JNDI configuration:
The code:
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.SessionCookieConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JndiSessionCookieConfigListener implements ServletContextListener {
private static final Logger logger = LoggerFactory.getLogger( JndiSessionCookieConfigListener.class );
private volatile Context jndiSessionCookieConfig;
private volatile SessionCookieConfig sessionCookieConfig;
#Override
public void contextInitialized( ServletContextEvent sce ) {
String listenerName = getClass().getSimpleName();
try {
logger.info( "JNDI override session cookie config found for {}", listenerName );
jndiSessionCookieConfig = (Context) new InitialContext().lookup(
"java:comp/env/" + listenerName );
}
catch ( NamingException e ) {
logger.info( "No JNDI override session cookie config found for {}", listenerName );
}
sessionCookieConfig = sce.getServletContext().getSessionCookieConfig();
String comment = getString( "comment" );
if ( comment != null ) {
logger.debug( "\t[comment]: [{}]", comment );
sessionCookieConfig.setComment( comment );
}
String domain = getString( "domain" );
if ( domain != null ) {
logger.debug( "\t[domain]: [{}]", domain );
sessionCookieConfig.setDomain( domain );
}
Boolean httpOnly = getBoolean( "http-only" );
if ( httpOnly == null ) {
sessionCookieConfig.setHttpOnly( true );
}
else {
logger.debug( "\t[http-only]: [{}]", httpOnly );
sessionCookieConfig.setHttpOnly( httpOnly );
}
Integer maxAge = getInteger( "max-age" );
if ( maxAge != null ) {
sessionCookieConfig.setMaxAge( maxAge );
}
String name = getString( "name" );
if ( name != null ) {
logger.debug( "\t[name]: [{}]", name );
sessionCookieConfig.setName( name );
}
String path = getString( "path" );
if ( path != null ) {
logger.debug( "\t[path]: [{}]", path );
sessionCookieConfig.setPath( path );
}
Boolean secure = getBoolean( "secure" );
if ( secure == null ) {
sessionCookieConfig.setSecure( true );
}
else {
logger.debug( "\t[secure]: [{}]", secure );
sessionCookieConfig.setSecure( secure );
}
}
#Override
public void contextDestroyed( ServletContextEvent sce ) {
}
private Boolean getBoolean( String name ) {
Object value;
try {
value = jndiSessionCookieConfig.lookup( name );
if ( value instanceof Boolean ) {
return (Boolean)value;
}
else {
return Boolean.valueOf( value.toString() );
}
}
catch ( NamingException e ) {
return null;
}
}
private Integer getInteger( String name ) {
Object value;
try {
value = jndiSessionCookieConfig.lookup( name );
if ( value instanceof Integer ) {
return (Integer)value;
}
else {
return Integer.valueOf( value.toString() );
}
}
catch ( NamingException e ) {
return null;
}
}
private String getString( String name ) {
Object value;
try {
value = jndiSessionCookieConfig.lookup( name );
return value.toString();
}
catch ( NamingException e ) {
return null;
}
}
}
Inside web.xml:
...
<listener>
<listener-class>
org.mitre.caasd.servlet.init.JndiSessionCookieConfigListener
</listener-class>
</listener>
...
In your context.xml:
...
<Environment name="JndiSessionCookieConfigListener/secure"
type="java.lang.String"
override="false"
value="true" />
...
This allows you to set all the session cookie configurations at runtime in the deployment environment. Thus, you could use the same webapp (war file) to do development locally (where you would not have https) and in production where you would ALWAYS want https.
Note, this approach is mentioned in the OWASP documentation
I have a bundle with just a cover that i have inserted in my timeline using mirror API. Now what i want is when a user clicks on the bundle, i get a custom menu clicking on which the backend is called to insert a set of cards again in the same bundle.
public class newsfeedbliss {
static String bundleId = "lunchRoulette" + UUID.randomUUID();
private static ArrayList<String> newstext = new ArrayList<String>();
static final String PROD_BASE_URL = "https://newsfeedbliss.appspot.com";
private static final String PROD_CALLBACK = PROD_BASE_URL + "/newsfeedcallback";
private static final String TEST_CALLBACK = "https://newsfeedbliss.appspot.com/newsfeedcallback";
public static void subscribe( HttpServletRequest req, String userId )
throws IOException
{
Mirror mirror = MirrorUtils.getMirror( req );
// START:subscribe
final String callbackUrl = "https://newsfeedbliss.appspot.com/newsfeedcallback";
Subscription tliSubscription = new Subscription()
.setCallbackUrl( callbackUrl )
.setVerifyToken( "a_secret_to_everybody" )
.setUserToken( userId )
.setCollection( "timeline" )
.setOperation( Collections.singletonList( "UPDATE" ) );
mirror.subscriptions().insert( tliSubscription ).execute();
// END:subscribe
// TODO: check if this user has subscribed, skip if already has
SubscriptionsListResponse subscriptions = mirror.subscriptions().list().execute();
for (Subscription sub : subscriptions.getItems()) {
System.out.println( sub );
}
}
public static TimelineItem buildarticlestimeline(
ServletContext ctx, String userId )
throws IOException, ServletException, ParserConfigurationException, SAXException
{
Mirror mirror = MirrorUtils.getMirror( userId );
Timeline timeline1 = mirror.timeline();
TimelineItem timelineItem1 = new TimelineItem()
.setText("Hello");
timeline1.insert( timelineItem1 ).executeAndDownloadTo( System.out );
return timelineItem1;
}
public static void insertSimpleTextTimelineItem( HttpServletRequest req )
throws IOException, ParserConfigurationException, SAXException
{
Mirror mirror = MirrorUtils.getMirror( req );
Timeline timeline = mirror.timeline();
TimelineItem timelineItem = new TimelineItem()
.setHtml("<article>\n <section>\n <p class=\"text-auto-size\">This <em class=\"yellow\">paragraph</em> auto-resizes according to the <strong class=\"blue\">HTML</strong> content length.\n </p>\n </section>\n</article>\n")
.setBundleId(bundleId)
.setIsBundleCover(true);
setSimpleMenuItems(timelineItem,true);
timeline.insert( timelineItem ).executeAndDownloadTo( System.out );
System.out.println("Hello hello");
}
public static void setSimpleMenuItems( TimelineItem ti, boolean hasRestaurant ) {
// Add blank menu list
ti.setMenuItems( new LinkedList<MenuItem>() );
ti.getMenuItems().add( new MenuItem().setAction( "READ_ALOUD" ) );
ti.getMenuItems().add( new MenuItem().setAction( "DELETE" ) );
List<MenuValue> menuValues = new ArrayList<MenuValue>(2);
menuValues.add( new MenuValue()
.setState( "DEFAULT" )
.setDisplayName( "Alternative" )
// .setIconUrl( "" )
);
menuValues.add( new MenuValue()
.setState( "PENDING" )
.setDisplayName( "Generating Alternative" ) );
ti.getMenuItems().add( new MenuItem()
.setAction( "CUSTOM" )
.setId( "ALT" )
.setPayload( "ALT" )
.setValues( menuValues )
);
}
}
This is my servlet file
public class NewsfeedblissServlet extends HttpServlet
{
private static final Logger log = Logger.getLogger(NewsfeedblissServlet.class.getName());
/** Accept an HTTP GET request, and write a random lunch type. */
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException
{
log.info("in do get");
try {
newsfeedbliss.insertSimpleTextTimelineItem( req );
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
log.info("called insert text");
resp.setContentType("text/html");
resp.getWriter().append( "Inserted Timeline Item" );
}
}
And this is the class i have written that has the code that i want to run on callback that detects custom menu click and inserts the cards.
public class TimelineUpdateServlet extends HttpServlet
{
protected void doPost(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException
{
System.out.println("Hey, Hello");
res.getWriter().append( "Inside update servlet" );
// Generate Notification from request body
JsonFactory jsonFactory = new JacksonFactory();
Notification notification =
jsonFactory.fromInputStream( req.getInputStream(), Notification.class );
// Get this user action's type
String userActionType = null;
if( !notification.getUserActions().isEmpty() )
userActionType = notification.getUserActions().get(0).getType();
//If this is a pinned timeline item, log who and which timeline item
if( "timeline".equals( notification.getCollection() )
&& "UPDATE".equals( notification.getOperation() )
&& "CUSTOM".equals( userActionType ) )
{
UserAction userAction = notification.getUserActions().get(0);
if( "ALT".equals( userAction.getPayload() ) )
{
// Add a new timeline item, and bundle it to the previous one
String userId = notification.getUserToken();
String itemId = notification.getItemId();
Mirror mirror = MirrorUtils.getMirror( userId );
Timeline timeline = mirror.timeline();
// Get the timeline item that owns the tapped menu
TimelineItem current = timeline.get( itemId ).execute();
String bundleId = current.getBundleId();
// If not a bundle, update this item as a bundle
if( bundleId == null ) {
bundleId = "lunchRoulette" + UUID.randomUUID();
current.setBundleId( bundleId );
timeline.update( itemId, current).execute();
}
// Create a new random restaurant suggestion
TimelineItem newTi=null;
try {
newTi = newsfeedbliss.buildarticlestimeline( getServletContext(), userId );
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
newTi.setBundleId( bundleId );
timeline.insert( newTi ).execute();
}
}
}
}
In your TimelineUpdateServlet code you must modify your original item to setIsBundleCover(true). The bundle cover will not have any menu items as it only acts as a parent for the items below it. Thus call
current.setIsBundleCover(true);
before you call
timeline.update( itemId, current).execute();
This should allow the new card to appear in the bundle properly.
I am trying to use the Mojarra JSF 2 compiler programatically with a view to checking the correctness of the xhtml of any pages.
I have got so far but the compiler is not erroring for tags that don't exist in a particular tag library. It does the standard XML namespace checks but if say rich:spacer is present it should error (it got removed in Richfaces 4.x). At runtime this check takes does take place.
Any thoughts? Here is my code:
#RunWith( PowerMockRunner.class )
#PrepareForTest( { WebConfiguration.class, FacesContext.class } )
public class XhtmlValidatorTest
{
#Test
public void test() throws IOException
{
WebConfiguration webConfiguration = PowerMock.createMock( WebConfiguration.class );
PowerMock.mockStatic( WebConfiguration.class );
WebConfiguration.getInstance();
PowerMock.expectLastCall().andReturn( webConfiguration ).anyTimes();
FaceletsConfiguration faceletsConfiguration = PowerMock.createMock( FaceletsConfiguration.class );
webConfiguration.getFaceletsConfiguration();
PowerMock.expectLastCall().andReturn( faceletsConfiguration ).anyTimes();
faceletsConfiguration.isProcessCurrentDocumentAsFaceletsXhtml(EasyMock.isA( String.class ) );
PowerMock.expectLastCall().andReturn(true).anyTimes();
faceletsConfiguration.isConsumeComments( EasyMock.isA( String.class) );
PowerMock.expectLastCall().andReturn(false).anyTimes();
faceletsConfiguration.isConsumeCDATA( EasyMock.isA( String.class ) );
PowerMock.expectLastCall().andReturn(false).anyTimes();
webConfiguration.isOptionEnabled(BooleanWebContextInitParameter.EnableMissingResourceLibraryDetection);
PowerMock.expectLastCall().andReturn( false ).anyTimes();
webConfiguration.isOptionEnabled(BooleanWebContextInitParameter.EnableCoreTagLibraryValidator );
PowerMock.expectLastCall().andReturn( true ).anyTimes();
FacesContext facesContext = PowerMock.createMock( FacesContext.class );
PowerMock.mockStatic( FacesContext.class );
FacesContext.getCurrentInstance();
PowerMock.expectLastCall().andReturn( facesContext ).anyTimes();
facesContext.isProjectStage( ProjectStage.Development );
PowerMock.expectLastCall().andReturn( false ).anyTimes();
Application application = PowerMock.createMock( Application.class );
facesContext.getApplication();
PowerMock.expectLastCall().andReturn( application ).anyTimes();
application.getExpressionFactory();
PowerMock.expectLastCall().andReturn( new org.jboss.el.ExpressionFactoryImpl() ).anyTimes();
PowerMock.replayAll();
long refreshPeriod = -1;
com.sun.faces.facelets.compiler.Compiler compiler = new SAXCompiler();
compiler.setValidating( true );
System.out.println( "Compiler.isValidating() " + compiler.isValidating() );
FaceletCache cache = new UnittestFaceletCacheFactory().getCache( refreshPeriod );
ResourceResolver resolver = new ResourceResolver()
{
#Override
public URL resolveUrl(String path)
{
URL url = null;
try
{
url = new URL( BASE_PATH + path );
}
catch (MalformedURLException e)
{
throw new RuntimeException( e );
}
return url;
}
};
DefaultFaceletFactory defaultFaceletFactory = new DefaultFaceletFactory( compiler, resolver, refreshPeriod, cache );
File file = new File( "WebContent" );
File[] files = file.listFiles();
for( File xhtmlFile : files )
{
if( xhtmlFile.isFile() )
{
String name = xhtmlFile.getName();
if( name.endsWith(".xhtml" ) )
{
System.out.println( "compiling: " + name );
defaultFaceletFactory.getFacelet( name );
}
}
}
}
The facelet cache factory used in the code is a hack:
package com.sun.faces.facelets.impl;
import javax.faces.view.facelets.FaceletCache;
public class UnittestFaceletCacheFactory
{
public FaceletCache getCache( long refreshPeriod )
{
return new DefaultFaceletCache( refreshPeriod );
}
}
Does anyone have a good approach towards unit testing their UserTypes?
By way of example, I have an object in my model called DateRange, which has a DatePoint start and DatePoint end. In addition to making range type operations available for two DateTimes, these objects let me adjust the precision for the task at hand (i.e., Day, Hour, Minute, etc.). When stored to the db for an application I'm working on, I just need to store the start and end as DateTime, no nulls allowed. I can't think of how to map this without a UserType, so I have:
/// <summary>User type to deal with <see cref="DateRange"/> persistence for time sheet tracking.</summary>
public class TimePeriodType : IUserType
{
public SqlType[] SqlTypes {
get {
var types = new SqlType[2];
types[0] = new SqlType(DbType.DateTime);
types[1] = new SqlType(DbType.DateTime);
return types;
}
}
public Type ReturnedType
{
get { return typeof(DateRange); }
}
/// <summary>Just return <see cref="DateRange.Equals(object)"/></summary>
public new bool Equals(object x, object y)
{
return x != null && x.Equals(y);
}
/// <summary>Just return <see cref="DateRange.GetHashCode"/></summary>
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var start = (DateTime)NHibernateUtil.DateTime.NullSafeGet(rs, names[0]);
var end = (DateTime)NHibernateUtil.DateTime.NullSafeGet(rs, names[1]);
return new DateRange(start, end, TimeSlice.Minute);
}
public void NullSafeSet(IDbCommand cmd, object value, int index) {
Check.RequireNotNull<DateRange>(value);
Check.RequireArgType<DateRange>(value);
var dateRange = ((DateRange)value);
NHibernateUtil.DateTime.NullSafeSet(cmd, dateRange.Start, index);
NHibernateUtil.DateTime.NullSafeSet(cmd, dateRange.End, index);
}
public object DeepCopy(object value) {
Check.RequireNotNull<DateRange>(value);
Check.RequireArgType<DateRange>(value);
var dateRange = ((DateRange) value);
return new DateRange(dateRange.Start, dateRange.End);
}
public bool IsMutable
{
get { return false; }
}
public object Replace(object original, object target, object owner) {
//because it is immutable so we can just return it as is
return original;
}
public object Assemble(object cached, object owner) {
//Used for caching, as it is immutable we can just return it as is
return cached;
}
public object Disassemble(object value) {
//Used for caching, as it is immutable we can just return it as is
return value;
}
}
}
Now I'm looking for a way to prove it works. Thanks in advance!
Cheers,
Berryl
I created a user type for System.Drawing.Color, and here is how I unit tested it with MSTest and Moq.
ColorUserType.cs:
public class ColorUserType : IUserType
{
public object Assemble( object cached, object owner )
{
return cached;
}
public object DeepCopy( object value )
{
return value;
}
public object Disassemble( object value )
{
return value;
}
public new bool Equals( object x, object y )
{
if(ReferenceEquals(x, y ) )
{
return true;
}
if( x == null || y == null )
{
return false;
}
return x.Equals( y );
}
public int GetHashCode( object x )
{
return x == null ? typeof( Color ).GetHashCode() + 473 : x.GetHashCode();
}
public bool IsMutable
{
get
{
return true;
}
}
public object NullSafeGet( IDataReader rs, string[] names, object owner )
{
var obj = NHibernateUtil.String.NullSafeGet( rs, names[0] );
if( obj == null )
{
return null;
}
return ColorTranslator.FromHtml( (string)obj );
}
public void NullSafeSet( IDbCommand cmd, object value, int index )
{
if( value == null )
{
( (IDataParameter)cmd.Parameters[index] ).Value = DBNull.Value;
}
else
{
( (IDataParameter)cmd.Parameters[index] ).Value = ColorTranslator.ToHtml( (Color)value );
}
}
public object Replace( object original, object target, object owner )
{
return original;
}
public Type ReturnedType
{
get
{
return typeof( Color );
}
}
public SqlType[] SqlTypes
{
get
{
return new[] { new SqlType( DbType.StringFixedLength ) };
}
}
}
ColorUserTypeTests.cs
[TestClass]
public class ColorUserTypeTests
{
public TestContext TestContext { get; set; }
[TestMethod]
public void AssembleTest()
{
var color = Color.Azure;
var userType = new ColorUserType();
var val = userType.Assemble( color, null );
Assert.AreEqual( color, val );
}
[TestMethod]
public void DeepCopyTest()
{
var color = Color.Azure;
var userType = new ColorUserType();
var val = userType.DeepCopy( color );
Assert.AreEqual( color, val );
}
[TestMethod]
public void DissasembleTest()
{
var color = Color.Azure;
var userType = new ColorUserType();
var val = userType.Disassemble( color );
Assert.AreEqual( color, val );
}
[TestMethod]
public void EqualsTest()
{
var color1 = Color.Azure;
var color2 = Color.Bisque;
var color3 = Color.Azure;
var userType = new ColorUserType();
var obj1 = (object)color1;
var obj2 = obj1;
Assert.IsFalse( userType.Equals( color1, color2 ) );
Assert.IsTrue( userType.Equals( color1, color1 ) );
Assert.IsTrue( userType.Equals( color1, color3 ) );
Assert.IsFalse( userType.Equals( color1, null ) );
Assert.IsFalse( userType.Equals( null, color1 ) );
Assert.IsTrue( userType.Equals( null, null ) );
Assert.IsTrue( userType.Equals( obj1, obj2 ) );
}
[TestMethod]
public void GetHashCodeTest()
{
var color = Color.Azure;
var userType = new ColorUserType();
Assert.AreEqual( color.GetHashCode(), userType.GetHashCode( color ) );
Assert.AreEqual( typeof( Color ).GetHashCode() + 473, userType.GetHashCode( null ) );
}
[TestMethod]
public void IsMutableTest()
{
var userType = new ColorUserType();
Assert.IsTrue( userType.IsMutable );
}
[TestMethod]
public void NullSafeGetTest()
{
var dataReaderMock = new Mock();
dataReaderMock.Setup( m => m.GetOrdinal( "white" ) ).Returns( 0 );
dataReaderMock.Setup( m => m.IsDBNull( 0 ) ).Returns( false );
dataReaderMock.Setup( m => m[0] ).Returns( "#ffffff" );
var userType = new ColorUserType();
var val = (Color)userType.NullSafeGet( dataReaderMock.Object, new[] { "white" }, null );
Assert.AreEqual( "ffffffff", val.Name, "The wrong color was returned." );
dataReaderMock.Setup( m => m.IsDBNull( It.IsAny() ) ).Returns( true );
Assert.IsNull( userType.NullSafeGet( dataReaderMock.Object, new[] { "black" }, null ), "The color was not null." );
dataReaderMock.VerifyAll();
}
[TestMethod]
public void NullSafeSetTest()
{
const string color = "#ffffff";
const int index = 0;
var mockFactory = new MockFactory( MockBehavior.Default );
var parameterMock = mockFactory.Create();
parameterMock.SetupProperty( p => p.Value, string.Empty );
var parameterCollectionMock = mockFactory.Create();
parameterCollectionMock.Setup( m => m[0] ).Returns( parameterMock.Object );
var commandMock = mockFactory.Create();
commandMock.Setup( m => m.Parameters ).Returns( parameterCollectionMock.Object );
var userType = new ColorUserType();
userType.NullSafeSet( commandMock.Object, ColorTranslator.FromHtml( color ), index );
Assert.AreEqual( 0, string.Compare( (string)( (IDataParameter)commandMock.Object.Parameters[0] ).Value, color, true ) );
userType.NullSafeSet( commandMock.Object, null, index );
Assert.AreEqual( DBNull.Value, ( (IDataParameter)commandMock.Object.Parameters[0] ).Value );
mockFactory.VerifyAll();
}
[TestMethod]
public void ReplaceTest()
{
var color = Color.Azure;
var userType = new ColorUserType();
Assert.AreEqual( color, userType.Replace( color, null, null ) );
}
[TestMethod]
public void ReturnedTypeTest()
{
var userType = new ColorUserType();
Assert.AreEqual( typeof( Color ), userType.ReturnedType );
}
[TestMethod]
public void SqlTypesTest()
{
var userType = new ColorUserType();
Assert.AreEqual( 1, userType.SqlTypes.Length );
Assert.AreEqual( new SqlType( DbType.StringFixedLength ), userType.SqlTypes[0] );
}
}
I was thinking I could mock / fake some of the dependencies here, but wound up deciding the best way to do this was by using a database.
Some things I learned along the way:
1) it's worth the effort when learning NHibernate techniques to have a dedicated set of tools including a way to quickly configure a db and test fixture for it (the same kinds of agile tools you'll need for everything else, really) and a dedicated test lab that you don't have an emotional investment in.
2) mocks do not lend themselves to interfaces you don't own, like IDataReader.
Cheers