Moq'ing successive calls with the same arguments

by abutler 30. March 2011 11:30

I like Moq.  For me it has made unit testing almost enjoyable, similar to how jQuery makes writing javascript more bearable.

 

Today I ran into an issue.  I'm mocking System.Data.IDataReader (I'm old school okay, but I'm writing unit tests, so give me a break).  So in my code, I call the Read() method of IDataReader until it returns false.

 

while (reader.Read())
{
	// old man code here
}

 

Moq is great when you setup calls to a method that are somewhat unique.  But in this case, each successive call has the same arguments (no argument).  So how can I get Read() to return true the first time, then false on any call after that?  Well it's probably in the Moq documentation somewhere, but forget that.  Here's my solution:

 

var readerMock = new Mock<System.Data.IDataReader>();
int row = 0;
readerMock.Setup(x => x.Read()).Returns(row++ == 0);

 

Except that doesn't seem to work quite right, since it evaluates upon setup, not on invocation.  Or something.  This seems to work:

 

var readerMock = new Mock<System.Data.IDataReader>();
int row = 0;
readerMock.Setup(x => x.Read()).Returns(() => row++ == 0);

Now it gets evaluated when it is invoked I think.  It works at any rate.  Plus the inclusion of a lambda expression gives the added benefit of making it appear that I know what I'm doing.

 

SQL Indexing in a Nutshell

by abutler 29. March 2011 10:55

 

Don't do it like this.  That is all

 

Changing fields to properties in a Serializable class

by abutler 28. March 2011 15:47

Recently, I began preparing our code for code analysis using FxCop.  In beginning the project, I chose to enforce rule CA1051, part of the Microsoft Minimum Recommended Rules ruleset.  Seems pretty straightforward right?  I'll just have to change some fields to properties, maybe deal with some scoping issues, refactor some code, etc.

 

One of the problems I ran up against while enforcing this rule was in dealing with some of our serializable classes.  Say you have a class like this one:

 

[Serializable]
public class FieldsAreAwesome
{
	public int WhyNot;
}

 

This presents a bit of a problem, as changing the class to 

 

[Serializable]
public class FieldsAreAwesome
{
	public int WhyNot { get; set; }
}

 

will break serialization (properties are not serialized).  So there are a few options:

 

  1. Change the field scope to private, add a property to encapsulate then change all dependent code
  2. Implement custom serialization
  3. Just break it and hope the blame goes to someone else

 

#2 is not really a possibility, since the properties would have to have a different name and the amount of dependent code to be changed is not trivial in our codebase.

 

#1 turns out to be the only real option.  To do custom serialization, you have to implement ISerializable, like this:

 

[Serializable]
public class FieldsAreAwesome : ISerializable
{
	public int WhyNot { get; set; }

	#region ISerializable Members

	private FieldsAreAwesome(SerializationInfo info, StreamingContext context)
	{
		WhyNot = info.GetInt32("WhyNot");
	}

	public void GetObjectData(SerializationInfo info, StreamingContext context)
	{
		info.AddValue("WhyNot", WhyNot);
	}

	#endregion
}

 

The new constructor is required for deserialization, your code will fail at runtime if you don't have a constructor with the right arguments.

 

Say we had a serialized object from this class that was persisted somewhere.  This object is then deserialized using the new class above.  Since the names match, it will work.  Subsequent serializations / deserializations will also work using the new class.

 

Did I test this?  Yes.  Is there a better way of doing this?  Probably.  Will something break in production as a result of this? I certainly hope not.

 

Update:

Here's a better way to write the serialization constructor:

private FieldsAreAwesome(SerializationInfo info, StreamingContext context)
{
	foreach (SerializationEntry entry in info)
	{
		switch (entry.Name)
		{
			case "WhyNot":
				WhyNot = (int)entry.Value;
				break;
		}
	}
}
One thing to keep in mind is that once you commit to doing serialization yourself, you have to account for pretty much every scenario.  Checking for existing fields in the serialization info BEFORE trying to deserialize that field is a good idea.  I found this out the hard way.

About the authors

We like to rock ICS Bank 2

Month List