Ci sono vari modi per farlo.
versione imperativo
Ecco una versione più semplice imperativo di quella prevista nel PO:
[Fact]
public void ImperativeTest()
{
var fixture = new Fixture();
var expected = fixture.CreateMany<SyncItem>(3).OrderBy(si => si.Key);
var unorderedItems = expected.Skip(1).Concat(expected.Take(1)).ToArray();
fixture.Inject(unorderedItems);
var sut = fixture.Create<SyncItemList>();
Assert.Equal(expected, sut);
}
Mentre the default number of many items is 3, penso che è meglio chiamarlo esplicitamente in questo tipo di test. L'algoritmo di scrambling utilizzato qui trae vantaggio dal fatto che dopo ordina una sequenza di tre (distinti) elementi, spostando il primo elemento sul retro deve corrispondere a un elenco non ordinato..
Tuttavia, il problema con questo approccio è che si basa sulla mutazione del fixture
, quindi è difficile da rifattorizzare un approccio più dichiarativo.
Versione personalizzata
Nel tentativo di refactoring per una versione più dichiarativo, è possibile prima incapsulare l'algoritmo di scrambling in a Customization:
public class UnorderedSyncItems : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new UnorderedSyncItemsGenerator());
}
private class UnorderedSyncItemsGenerator : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
var t = request as Type;
if (t == null ||
t != typeof(SyncItem[]))
return new NoSpecimen(request);
var items = ((IEnumerable)context
.Resolve(new FiniteSequenceRequest(typeof(SyncItem), 3)))
.Cast<SyncItem>();
return items.Skip(1).Concat(items.Take(1)).ToArray();
}
}
}
risolvere una new FiniteSequenceRequest(typeof(SyncItem), 3))
è semplicemente il debolmente tipizzato (non generico) modo di creare una sequenza finita di istanze SyncItem
; è quello che fa CreateMany<SyncItem>(3)
dietro le quinte.
Ciò consente di refactoring del test per:
[Fact]
public void ImperativeTestWithCustomization()
{
var fixture = new Fixture().Customize(new UnorderedSyncItems());
var expected = fixture.Freeze<SyncItem[]>().OrderBy(si => si.Key);
var sut = fixture.Create<SyncItemList>();
Assert.Equal(expected, sut);
}
Si noti l'uso del metodo Freeze. Ciò è necessario, poiché la personalizzazione UnorderedSyncItems
modifica solo le istanze create da SyncItem[]
; crea ancora un nuovo array ogni volta che riceve una richiesta di farlo. Freeze
assicura che lo stesso array venga riutilizzato ogni volta, anche quando fixture
crea l'istanza sut
.
convenzione basata test
La prova di cui sopra può essere riscritta ad una prova dichiarativa, convenzione basata, con l'introduzione di un attributo [UnorderedConventions]
:
public class UnorderedConventionsAttribute : AutoDataAttribute
{
public UnorderedConventionsAttribute()
: base(new Fixture().Customize(new UnorderedSyncItems()))
{
}
}
Questa è semplicemente la colla dichiarativa per applicare la personalizzazione UnorderedSyncItems
. Il test diventa ora:
[Theory, UnorderedConventions]
public void ConventionBasedTest(
[Frozen]SyncItem[] unorderedItems,
SyncItemList sut)
{
var expected = unorderedItems.OrderBy(si => si.Key);
Assert.Equal(expected, sut);
}
Si noti l'uso degli attributi [UnorderedSyncItems]
e [Frozen]
.
Questo test è molto sintetico, ma potrebbe non essere quello che cerchi. Il problema è che il cambiamento di comportamento è ora nascosto nell'attributo [UnorderedSyncItems]
, quindi è piuttosto implicito ciò che sta accadendo. Preferisco utilizzare la stessa personalizzazione come un insieme di convenzioni per un'intera suite di test, quindi non mi piace introdurre variazioni del caso di test a questo livello. Tuttavia, se le tue convenzioni stabiliscono che le istanze SyncItem[]
devono essere sempre senza, questa convenzione è buona.
Tuttavia, se si desidera utilizzare solo array non ordinati per alcuni casi di test, l'utilizzo di un attributo [AutoData]
non è l'approccio ottimale.
banco di prova dichiarativa
Sarebbe bello se si potrebbe semplicemente applicare un attributo a livello di parametri, proprio come l'attributo [Frozen]
- forse la loro combinazione, come [Unordered][Frozen]
. Tuttavia, questo approccio non funziona.
Nota dagli esempi precedenti che l'ordine è valido. È necessario applicare UnorderedSyncItems
prima del blocco, in caso contrario, la matrice che si sta bloccando potrebbe non essere garantita non ordinata.
Il problema con [Unordered][Frozen]
parametro a livello di attributi è che, mentre si compila, il framework .NET non garantisce l'ordine degli attributi quando la libreria colla AutoFixture xUnit.net legge e applica gli attributi.
Invece, è possibile definire un singolo attributo di applicare, in questo modo:
public class UnorderedFrozenAttribute : CustomizeAttribute
{
public override ICustomization GetCustomization(ParameterInfo parameter)
{
return new CompositeCustomization(
new UnorderedSyncItems(),
new FreezingCustomization(parameter.ParameterType));
}
}
(FreezingCustomization
prevede l'implementazione sottostante dell'attributo [Frozen]
.)
Ciò consente di scrivere questo test:
[Theory, AutoData]
public void DeclarativeTest(
[UnorderedFrozen]SyncItem[] unorderedItems,
SyncItemList sut)
{
var expected = unorderedItems.OrderBy(si => si.Key);
Assert.Equal(expected, sut);
}
Si noti che questo test dichiarativo utilizza l'attributo [AutoData]
predefinito senza alcuna personalizzazione zazioni, poiché lo scrambling viene ora applicato dall'attributo [UnorderedFrozen]
a livello di parametro.
Ciò consentirebbe anche di utilizzare una serie di (altre) convenzioni della suite di test incapsulate in un attributo -derived [AutoData]
e utilizzare ancora [UnorderedFrozen]
come meccanismo di attivazione.
Come appare il 'SyncItemList'? –
Sapete cosa sarebbe veramente interessante se ci fosse un attributo e un tipo di richiesta Non Ordinato, una specie di sequenza finita. Avresti solo bisogno di 3 o più oggetti unici e potresti garantire una sequenza fuori sequenza. – cocogorilla
Posso chiedere quale versione di AutoFixture stai usando? [I numeri sono casuali] (https://github.com/AutoFixture/AutoFixture/wiki/AutoFixture-3.0-Release-Notes#numbers-are-random) in AutoFixture 3. –