A seconda del tipo di parametri che è necessario passare al falso manuale, è possibile utilizzare un attributo parametrizzato, simile a quello predefinito di AutoFixture .
Alla luce di questi
public interface IHardToMockDependency
{
string Value { get; }
}
public class FakeHardToMockDependency : IHardToMockDependency
{
private readonly string _value;
public FakeHardToMockDependency(string value)
{
_value = value;
}
#region IHardToMockDependency Members
public string Value
{
get { return this._value; }
}
#endregion IHardToMockDependency Members
}
si crea un ICustomization
implementazione che racconta un oggetto apparecchio come creare Un'implementazione dell'interfaccia IHardToFakeDependency
:
public class FakeHardToMockDependencyCustomization : ICustomization
{
private readonly string _value;
public FakeHardToMockDependencyCustomization(string value)
{
_value = value;
}
#region ICustomization Members
public void Customize(IFixture fixture)
{
fixture.Register<IHardToMockDependency>(() => new FakeHardToMockDependency(this._value));
}
#endregion ICustomization Members
}
Si noti che questo ha bisogno di sapere la stringa che si desidera passare, ovviamente.
Successivamente, è arrotolare con le altre personalizzazioni che si desidera utilizzare in un CompositeCustomization
:
public class ManualFakeTestConventions : CompositeCustomization
{
public ManualFakeTestConventions(string value)
: base(new FakeHardToMockDependencyCustomization(value), new AutoMoqCustomization())
{
}
}
Assicurati sempre di mettere le personalizzazioni in ordine dal più specifico al più generale, come spiegato here da Mark Seemann.
Ora si crea un AutoDataAttribute
applicazione che utilizza questa personalizzazione:
public class ManualFakeAutoDataAttribute : AutoDataAttribute
{
public ManualFakeAutoDataAttribute(string value)
: base(new Fixture().Customize(new ManualFakeTestConventions(value)))
{
}
}
Questo può ora essere utilizzato allo stesso modo come un InlineAutoDataAttribute
:
public class ManualFakeTests
{
[Theory, ManualFakeAutoData("iksdee")]
public void ManualFake(IHardToMockDependency fake)
{
Assert.IsType<FakeHardToMockDependency>(fake);
Assert.Equal("iksdee", fake.Value);
}
}
Si può anche avere iniettato in un auto-creato esempio SUT subito applicando l'attributo [Frozen]
al parametro Teoria:
[Theory, ManualFakeAutoData("iksdee")]
public void SutWithManualFake([Frozen] IHardToMockDependency fake, MySut sut)
{
}
questo creerà un'istanza MySut
e l'istanza IHardToMockDependency
necessario per il costruttore, per il quale avete dato AutoFixture una regola nel FakeHardToMockDependencyCustomization
, e anche darvi quel caso come variabile fake
.
Nota che il non blocco del falso ti darebbe comunque un'istanza corretta FakeHardToMockDependency
e ne inserirò uno nel sut, ma questi sarebbero distinti, dato che abbiamo registrato un delegato di fabbrica nella personalizzazione. Il blocco dell'istanza fa sì che l'apparecchiatura restituisca sempre la stessa istanza per le successive richieste per l'interfaccia.
Questo ha alcune avvertenze, però:
- Si hanno alcun riferimento alla stringa si passa come un parametro, in modo da avere in là come una stringa letterale due volte. Puoi aggirare questo con costanti di stringa all'interno della classe di test, per esempio.
- Il numero di tipi che possono essere utilizzati come parametri di attributo NET è limitata. Finché è necessario solo tipi di base, si dovrebbe andare bene, ma chiamando costruttori o simili nella lista dei parametri non è possibile.
- Dovreste solo utilizzare questo attributo quando si ha bisogno di un esempio se
IHardToFakeDependency
; altrimenti dovrai sempre passare un parametro stringa comunque. Se si dispone di una serie di personalizzazioni standard, è necessario utilizzare, creare un altro attributo che contiene solo quelle.
- Se avete bisogno le capacità del
InlineAutoDataAttribute
, allo stesso tempo, è anche necessario creare un altro attributo che unisce le caratteristiche di entrambi.
A seconda delle circostanze concrete, si potrebbe anche voler guardare xUnit.net di PropertyDataAttribute
, ma ho quasi mai trovare me stesso utilizzando tale.
In generale, a mio parere, capire come lavorare con le personalizzazioni e gli attributi di autodata e quando e come crearne uno sono la chiave per utilizzare in modo efficace AutoFixture e davvero far sì che vi risparmi lavoro.
Se si scrive spesso codice in un dominio specifico che è necessario testare, potrebbe essere opportuno creare una libreria contenente personalizzazioni, attributi e oggetti stub che saranno sempre pronti per l'uso dopo averlo trascinato accanto a xUnit .net, AutoFixture e Moq. So di essere dannatamente felice di aver costruito il mio.
Oh, e anche: Avere una dipendenza difficile da simulare potrebbe essere il punto in un problema di progettazione. Perché è così difficile deridere?
Ora vai e prendi il martello d'oro dalla mia mano. ;-) Ma che differenza fa 'internal' qui, al contrario di 'public'? Il SUT non può vedere la proprietà in entrambi i casi, perché ottiene solo l'interfaccia. – TeaDrivenDev
@GCATNM Non importa se la proprietà Result è 'internal' o' public'. Personalmente, lo renderei pubblico, ma l'OP forniva il falso come una classe "interna", quindi volevo solo cambiare il meno possibile per rendere le modifiche suggerite più accettabili :) –
Wow, tre grandi risposte, e uno dall'autore stesso! Allentare gli invarianti del falso va bene. Grazie! Accettare solo una risposta sarà dura - dovrò provarle. – TrueWill