Create an instance of FormsAuthenticationTicket with a valid XML string in UserData

StackOverflow https://stackoverflow.com/questions/23508554

  •  16-07-2023
  •  | 
  •  

Domanda

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.

È stato utile?

Soluzione

AutoFixture picks the modest constructor (by default) and since userData is not part of the modest constructor we need to customize two things here:

  1. Change the constructor strategy for a single type (FormsAuthenticationTicket).
  2. Supply a custom value to the 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 @>

Altri suggerimenti

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:

  • Version: 85
  • Name: name1e8fb8b1-5879-4256-8729-ca6afeb1bd80
  • IssueDate: 12/3/2015 8:00:05 AM
  • IsPersistent: True
  • UserData: <root>My Valid XML Value Here</root>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top