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

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

  •  16-07-2023
  •  | 
  •  

سؤال

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:

  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 @>

نصائح أخرى

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>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top