I want to create an instance of FormsAuthenticationTicket (over which I have no control, part of System.Web.Security) using Autofixture AND making sure that the UserData (of type string) contains a valid XML string
var testTicket = fixture.Create<FormsAuthenticationTicket>();
The problem is that UserData can only be set when instantiating the object using the following constructor:
public FormsAuthenticationTicket(int version, string name, DateTime issueDate, DateTime expiration, bool isPersistent, string userData);
Where "userData" is a valid XML string.
I can configure this type to use the greediest constructor, but that does not solves the problem of providing a valid XML string to userData.
I could freeze the string type to make it always return a valid XML string, but I also care about other string values on my test.
I am thinking a possible approach is to customize the algorithm for string generation...but I have not parameters to know when to provide the XML like string.
AutoFixture picks the modest constructor (by default) and since userData
is not part of the modest constructor we need to customize two things here:
userData
constructor argument.The following Customization encapsulates both:
internal class UserDataCustomization : ICustomization
{
private readonly string userData;
public UserDataCustomization(string userData)
{
this.userData = userData;
}
public void Customize(IFixture fixture)
{
fixture.Customize<FormsAuthenticationTicket>(c =>
c.FromFactory(
new MethodInvoker(
new GreedyConstructorQuery())));
fixture.Customizations.Add(new UserDataBuilder(this.userData));
}
private class UserDataBuilder : ISpecimenBuilder
{
private readonly string userData;
public UserDataBuilder(string userData)
{
this.userData = userData;
}
public object Create(object request, ISpecimenContext context)
{
var pi = request as ParameterInfo;
if (pi != null && pi.Name == "userData")
return this.userData;
return new NoSpecimen();
}
}
}
The following test passes:
[Fact]
public void UserDataIsCorrect()
{
var expected = "<foo></foo>";
var fixture = new Fixture();
fixture.Customize(new UserDataCustomization(expected));
var actual = fixture.Create<FormsAuthenticationTicket>();
Assert.Equal(expected, actual.UserData);
}
Hope that helps.
FWIW, here is also the same in F#:
open Ploeh.AutoFixture
open Ploeh.AutoFixture.Kernel
open System
open System.Reflection
open System.Web.Security
type UserDataCustomization (userData) =
let builder = {
new ISpecimenBuilder with
member this.Create(request, context) =
match request with
| :? ParameterInfo as pi
when pi.Name = "userData" -> box userData
| _ -> NoSpecimen request |> box }
interface ICustomization with
member this.Customize fixture =
fixture.Customize<FormsAuthenticationTicket>(fun c ->
c.FromFactory(
MethodInvoker(
GreedyConstructorQuery())) :> ISpecimenBuilder)
fixture.Customizations.Add builder
The following test passes:
open Xunit
open Swensen.Unquote.Assertions
[<Fact>]
let UserDataIsCorrect () =
let expected = "<foo></foo>"
let fixture = Fixture().Customize(UserDataCustomization(expected))
let actual = fixture.Create<FormsAuthenticationTicket>()
test <@ expected = actual.UserData @>
Freezing the string would work, but isn't desirable since it would affect all other generated strings as well.
I use this class to customize a specific constructor argument:
public class GenericArgCustomization<T> : ISpecimenBuilder
{
private readonly string name;
private readonly T value;
public GenericArgCustomization(string name, T value)
{
if (String.IsNullOrEmpty(name))
throw new ArgumentException("Name is required", "name");
this.name = name;
this.value = value;
}
public object Create(object request, ISpecimenContext context)
{
var pi = request as ParameterInfo;
if (pi == null)
return new NoSpecimen(request);
if (pi.ParameterType != typeof(T) || pi.Name != this.name)
return new NoSpecimen(request);
return this.value;
}
}
Next, you can use it by specifying the generic type (string
in this case) and the constructor argument name that you want to customize (userData
in this case). This is case-sensitive so it matches the argument name you've used in the class.
Example:
var xml = "<root>My Valid XML Value Here</root>";
var customUserData = new GenericArgCustomization<string>("userData", xml);
var fixture = new Fixture();
fixture.Customizations.Add(customUserData);
var item = fixture.Create<FormsAuthenticationTicket>();
Now the item gets created with data such as:
85
name1e8fb8b1-5879-4256-8729-ca6afeb1bd80
12/3/2015 8:00:05 AM
True
<root>My Valid XML Value Here</root>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With