2013-06-14 3 views
6

mam następujące typy:Jak skonfigurować AutoFixture do używania wartości wyliczeniowej jako materiału siewnego podczas tworzenia wielu określonego typu?

public enum Status 
{ 
    Online, 
    Offline 
} 

public class User 
{ 
    private readonly Status _status; 
    public User(Status status) { _status = status; } 
    public Status Status {get {return _status; }} 
    public string Name {get;set;} 
} 

Teraz, podczas wykonywania fixture.CreateMany<User> chcę AutoFixture aby powrócić dwa Users, po jednej statusu. Wszystkie inne właściwości - takie jak Name - powinny być wypełnione anonimowymi danymi.

Pytanie:
Jak skonfigurować AutoFixture, aby to zrobić?


Próbowałem następującą to:

  1. Rejestracja kolekcję, aktualności się przedmiotem User:

    fixture.Register(
        () => Enum.GetValues(typeof(Status)).Cast<Status>().Select(s => 
         new User(s))); 
    

    Problem z tego podejścia jest to, że AutoFixture nie wypełnia drugi właściwości takie jak Name

  2. Dostosuj User używać fabrykę i zarejestrować zbiór, który używa fixture.Create:

    f.Customize<User>(c => c.FromFactory((Status s) => new User(s))); 
        f.Register(() => 
         Enum.GetValues(typeof(Status)) 
          .Cast<Status>() 
          .Select(s => (User)f.Create(new SeededRequest(typeof(User), s), 
                 new SpecimenContext(f)))); 
    

    To nie działało. Nasienie nie jest używane.

Odpowiedz

5

Można to zrobić:

var users = new Fixture().Create<Generator<User>>(); 

var onlineUser = users.Where(u => u.Status == Status.Online).First(); 
var offlineUser = users.Where(u => u.Status == Status.Offline).First(); 

Jeśli używasz AutoFixture.Xunit, deklaratywny odpowiednik to:

[Theory, AutoData] 
public void CreateOneOfEachDeclaratively(Generator<User> users) 
{ 
    var onlineUser = users.Where(u => u.Status == Status.Online).First(); 
    var offlineUser = users.Where(u => u.Status == Status.Offline).First(); 

    // Use onlineUser and offlineUser here... 
} 
+0

Dzięki za odpowiedź. Niestety, to nie działa, jeśli właściwość 'Status' nie jest" tylko do odczytu ". 'First'" never "return (Czekałem co najmniej 30 sekund). –

+0

Słowo kluczowe "readonly" nie ma z tym nic wspólnego. Właśnie usunąłem słowo kluczowe "readonly" z mojej repro i nie zmieniło wyniku. –

+0

Nie mówię o tym, że pole zaplecza nie jest tylko do odczytu. Mówię o tym, że właściwość * nie jest tylko do odczytu, tj. 'Public Status Status {get; zestaw; } '. –

3

Użytkownik może zadeklarować i zastosować dostosowanie, np. StatusGenerator:

var fixture = new Fixture(); 
fixture.RepeatCount = 2; 
fixture.Customizations.Add(new StatusGenerator()); 

var result = fixture.CreateMany<User>(); 

hipotetyczny realizacja StatusGenerator mogłyby być następujące:

internal class StatusGenerator : ISpecimenBuilder 
{ 
    private readonly Status[] values; 
    private int i; 

    internal StatusGenerator() 
    { 
     this.values = 
      Enum.GetValues(typeof(Status)).Cast<Status>().ToArray(); 
    } 

    public object Create(object request, ISpecimenContext context) 
    { 
     var pi = request as ParameterInfo; 
     if (pi == null || !pi.ParameterType.IsEnum) 
      return new NoSpecimen(request); 

     return this.values[i == this.values.Length - 1 ? i = 0 : ++i]; 
    } 
} 
+0

Nicea, dzięki. Czy byłoby to możliwe bez ustawienia 'RepeatCount'? Jak rozumiem, wpływa to na * wszystkie * wywołania do 'CreateMany', a nie tylko do' User' ... –

+0

Tak, jako @MarkSeemann [odpowiedział] (http://stackoverflow.com/a/17170033/467754):) –

2

podstawie Mark's answer, to co używam teraz:

fixture.Customize<User>(c => c.Without(x => x.Status)); 
fixture.Customize<IEnumerable<User>>(
    c => 
    c.FromFactory(
     () => Enum.GetValues(typeof(Status)).Cast<Status>() 
        .Select(s => users.First(u => u.Status == s)))); 

fixture.Create<IEnumerable<User>>(); // returns two Users 
2

Wiem, że jest już odpowiedział Generator było bardzo interesujące odkrycie. Myślę, że istnieje znacznie prostsze podejście do tego problemu.

 var numberOfEnumValues = Enum.GetValues(typeof(Status)).Length; 
     var users = fixture.CreateMany<User>(numberOfEnumValues); 

W przypadku, gdy konstruktor jest bardziej skomplikowany, z wieloma wartościami statusu lub model ma ustawniki właściwości typu statusu. Wtedy generalnie masz problem, a generator także może wybuchnąć.

Powiedzmy, że:

public class SuperUser : User 
    { 
     public SuperUser(Status status, Status shownStatus): base(status) 
     { 
     } 
    } 

Wtedy to nigdy nie będą oceniane:

var users = fixture.Create<Generator<SuperUser>>(); 
    var offlineUser = users.Where(u => u.Status == Status.Offline).First();