Generics in C # Test Class

1

Sto imparando i test unitari in C #. Languages_Service e PlaceOfWork_Service sono classi per servizi SOAP generati da Microsoft Navision. Esistono diversi metodi molto simili (vedi i due esempi sotto).

[TestMethod]
public void Languages()
{
    string serviceUrl = Helper.GetBaseUrl() + "Languages";
    Languages_Service service = new Languages_Service();
    service.Credentials = Helper.GetCredentials();
    service.Url = serviceUrl;
    Languages_Filter[] filter = { new Languages_Filter() };
    service.ReadMultiple(filter, null, 0);
}
[TestMethod]
public void PlaceOfWork()
{
    string serviceUrl = Helper.GetBaseUrl() + "PlaceOfWork";
    PlaceOfWork_Service service = new PlaceOfWork_Service();
    service.Credentials = Helper.GetCredentials();
    service.Url = serviceUrl;
    PlaceOfWork_Filter[] filter = { new PlaceOfWork_Filter() };
    service.ReadMultiple(filter, null, 0);
}

Il metodo ReadMultiple di Language_Service ha il seguente aspetto:

public Languages[] ReadMultiple([System.Xml.Serialization.XmlElementAttribute("filter")] Languages_Filter[] filter, string bookmarkKey, int setSize)
    {
        object[] results = this.Invoke("ReadMultiple", new object[] {
        filter,
        bookmarkKey,
        setSize});
     return ((Languages[])(results[0]));}

Metodo ReadMultiple di PlaceOfWork_Service ha il seguente aspetto:

public PlaceOfWork[] ReadMultiple([System.Xml.Serialization.XmlElementAttribute("filter")] PlaceOfWork_Filter[] filter, string bookmarkKey, int setSize) {
        object[] results = this.Invoke("ReadMultiple", new object[] {
                    filter,
                    bookmarkKey,
                    setSize});
        return ((PlaceOfWork[])(results[0]));
    }

Come si potrebbe scrivere qualcosa come il (pseudo) codice qui sotto?

[TestMethod]
public void GunAndSmokeTestAllSerivces()
{
    Dictionary<String, List<Type>> servicesToTest = new Dictionary<String, List<Type>>();
    servicesToTest.Add("Languages", new List<Type>(new Type[] { typeof(Languages_Service), typeof(Languages_Filter) }));
    servicesToTest.Add("PlaceOfWork", new List<Type>(new Type[] { typeof(PlaceOfWork_Service), typeof(PlaceOfWork_Filter) }));
    foreach (KeyValuePair<String, List<Type>> entry in servicesToTest)
    {
        var t1 = entry.Value.ToArray()[0];
        var t2 = entry.Value.ToArray()[1];
        SoapHttpClientProtocol service = (SoapHttpClientProtocol)Activator.CreateInstance(t1);
        service.Credentials = Helper.GetCredentials();
        service.Url = Helper.GetBaseUrl() + entry.Key;
        object[] filter = { (object)Activator.CreateInstance(t2) };
        service.ReadMultiple(filter, null, 0);
    }
}

Languages_Service e PlaceOfWork_Service sono sottoclassi di System.Web.Services.Protocols.SoapHttpClientProtocol .

    
posta lozsui 22.04.2015 - 11:11
fonte

2 risposte

1

Probabilmente non vuoi fare esattamente quello che descrivi perché allora avresti un singolo metodo di test che copre più casi di test. Questo non è eccezionale perché significa che i test perdono granularità, quindi: -

  • Diventa poco chiaro quando un test fallisce dove potrebbe trovarsi esattamente il bug, ad es. quale 'Servizio.
  • C'è un rischio maggiore di un motivo per cui un mascheramento fallisce. cioè ci sono in realtà più bug, ma poiché il test fallisce dopo il primo, non sei a conoscenza del resto.

Una soluzione più semplice e probabilmente più applicabile è solo per fare ciò che fai normalmente quando c'è un codice ripetuto come questo: estraetelo in un metodo parametrizzato:

private void Test<TService,TFilter>(string urlSuffix) 
    where TService : SoapHttpClientProtocol, new() 
    where TFilter : new() 
{
    string serviceUrl = Helper.GetBaseUrl() + urlSuffix;
    TService service = new TService();
    service.Credentials = Helper.GetCredentials();
    service.Url = serviceUrl;
    TFilter[] filter = { new TFilter() };
    service.ReadMultiple(filter, null, 0);
}

[TestMethod]
public void PlaceOfWork()
{
    Test<PlaceOfWork_Service,PlaceOfWork_Filter>("PlaceOfWork");
}

Questa è la versione base, solo per darti un'idea approssimativa. Come dicevano i commenti, questo sembra incompleto senza affermazioni e potrebbero esserci modi migliori per dividerlo in più metodi. Dovresti anche trovare nomi migliori in base alla tua comprensione di cosa sono esattamente questi test.

Alcuni problemi da prendere in considerazione:

  • Questo non funzionerà out of the box perché ReadMultiple non è un metodo su SoapHttpClientProtocol . Avrai bisogno di una superclasse o di un'interfaccia con questo metodo e avrai come vincolo il tuo parametro generico TService .
  • Allo stesso modo, TFilter potrebbe richiedere un vincolo simile
  • Se i due precedenti risultano essere un problema, puoi lasciare i tuoi metodi di test individuali incaricati di chiamare quella riga ReadMultiple , oppure potrebbero fornire un Action<TService,TFilter[]> o simile
  • Se non puoi effettivamente mantenere quel vincolo di new() , dovrai invece rimuoverlo e avere un parametro TService sul metodo, e lasciare che il singolo test sia responsabile della costruzione.
risposta data 22.04.2015 - 14:12
fonte
0

Ho modificato leggermente l'idea di Ben Aaronson:

public class NavisionSoapService : SoapHttpClientProtocol
{
    public object[] ReadMultiple([System.Xml.Serialization.XmlElementAttribute("filter")] object[] filter, string bookmarkKey, int setSize)
    {
        return this.Invoke("ReadMultiple", new object[] {filter,bookmarkKey,setSize});
    }
}

    [TestClass]
    public class SoapServiceGunSmokeTests
    {
        private void Test<TService>(string urlSuffix)
            where TService : NavisionSoapService, new()
        {
            string serviceUrl = Helper.GetBaseUrl() + urlSuffix;
            TService service = new TService();
            service.Credentials = Helper.GetCredentials();
            service.Url = serviceUrl;
            // something is fishy here: object [] filter = [System.Xml.Serialization.XmlElementAttribute("filter")] object[] filter;
            service.ReadMultiple(filter,null,0);
        }
    [TestMethod]
    public void PlaceOfWork()
    {
        Test<PlaceOfWork_Service>("PlaceOfWork");
    }
}

Ora, ottengo: "Il tipo 'PlaceOfWork_Service' non può essere utilizzato come parametro di tipo 'TService' nel tipo generico o nel metodo 'MyProject.SoapServiceGunSmokeTests.Test (stringa)'. Non esiste alcuna conversione implicita del riferimento da 'MyProject. WSPlaceOfWork.PlaceOfWork_Service 'a' MyProject.NavisionSoapService '.

Ovviamente, sto facendo qualcosa di sbagliato con la superclasse NavisionSoapService. Come devo farlo?

Inoltre, in qualche modo non riesco a importare System.Xml.Serialization I get "Il tipo o il nome dello spazio dei nomi 'Xml' non esiste nello spazio dei nomi 'Sistema' (ti manca un riferimento all'assembly?)". Ho aggiunto System.Xml.Serialization (versione 4.0.0.0) come riferimento.

Infine, sto facendo qualcosa di sbagliato creando un'istanza di filtro (vedi il codice sopra).

    
risposta data 28.04.2015 - 16:42
fonte

Leggi altre domande sui tag