Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

δεν έχω δουλέψει πολύ με Interfaces λόγω του ότι τα συναντάμε πιο συχνά σε μεγάλα projects, αλλά έχω δει μερικά projects και θεωρώ ότι γίνεται ανούσια χρήση των interfaces. του τύπου ρε παιδί μου να έχεις 15 classes και για τα 10 απο αυτα να εχεις και απο 1 Interface. και κανενα Interface να μην γινεται Implement σε πανω απο 1 κλαση.

 

κάποιο βιβλίο ή έστω online course για Interfaces best practices?

Δημοσ.

Έχεις προσπαθήσει να καταλάβεις τι θέλουν να πετύχουν κάνοντάς το αυτό; Π.χ. έχω δουλέψει σε components όπου υπήρχαν περισσότερα interfaces από concrete classes, και σαφώς υπήρχε νόημα.

 

Μπορεί και να τον παίζουν, τίποτα δεν αποκλείεται, αλλά μάλλον αυτό που σου χρειάζεται είναι κατανόηση και όχι τυφλοσούρτες "best practices".

Δημοσ.

Δύο (αλληλένδετοι) λόγοι που χρειάζονται interfaces σε κάθε κλάση είναι το Link.png Site: dependency injection και το Link.png Site: unit testing .

Φυσικά υπάρχουν και πολλοί άλλοι λόγοι, όπως π.χ. ο πολυμορφισμός.

Δημοσ.

Δύο (αλληλένδετοι) λόγοι που χρειάζονται interfaces σε κάθε κλάση είναι το Link.png Site: dependency injection και το Link.png Site: unit testing .

Φυσικά υπάρχουν και πολλοί άλλοι λόγοι, όπως π.χ. ο πολυμορφισμός.

 

Για το dependency injection το καταλαβαίνω και συμφωνώ , για το unit testing τι δουλειά έχει?

Δημοσ.

Για να μπορείς να κάνεις mock κλάσεις που δεν χρειάζεται ή δεν πρέπει να εξετάζεις μέσα στο εκάστοτε test. Δες τον εξής (χαζό) κώδικα και σκέψου πως θα τον έκανες testable:

public class LogCleaner
{
	private DbContext database;
	
	public LogCleaner(DbContext db)
	{
		this.database = db;
	}
	
	public void CleanOldLogEntries(int userId)
	{
		var logs = this.database.Users.Find(userId).Logs.OrderBy(x => x.Date).Take(100);
		foreach (var entry in user.Logs)
		{
			File.Remove(entry.Path);
		}
	}
}

// test
var lc = new LogCleaner(???); // Αν κανω instance απο DbContext εδω, το test θα προσπαθήσει να βρει μια ΒΔ και να τρέξει query
lc.CleanOldLogEntries(1);	// Ο κωδικας θα προσπαθησει οντως να σβησει αρχεια απο τον δισκο


Δημοσ.

 

Για να μπορείς να κάνεις mock κλάσεις που δεν χρειάζεται ή δεν πρέπει να εξετάζεις μέσα στο εκάστοτε test. Δες τον εξής (χαζό) κώδικα και σκέψου πως θα τον έκανες testable:

public class LogCleaner
{
	private DbContext database;
	
	public LogCleaner(DbContext db)
	{
		this.database = db;
	}
	
	public void CleanOldLogEntries(int userId)
	{
		var logs = this.database.Users.Find(userId).Logs.OrderBy(x => x.Date).Take(100);
		foreach (var entry in user.Logs)
		{
			File.Remove(entry.Path);
		}
	}
}

// test
var lc = new LogCleaner(???); // Αν κανω instance απο DbContext εδω, το test θα προσπαθήσει να βρει μια ΒΔ και να τρέξει query
lc.CleanOldLogEntries(1);	// Ο κωδικας θα προσπαθησει οντως να σβησει αρχεια απο τον δισκο


 

Γιατί δεν μπορώ να τις κάνω mock? Μια χαρά μπορώ με mockito , αν σε απασχολεί η private μπορείς να κάνεις .spy() και να λύσεις το πρόβλημα σου. 

Το Interface σε τι σε σώζει δλδ? Unit test πάλι στο implementation θα γράψεις, δεν νομίζω ότι ισχύει αυτό που λές , εκτός αν καταλαβαίνω κάτι λάθος :)

Δημοσ. (επεξεργασμένο)

Για Java δεν είμαι σίγουρος, αλλά π.χ. σε C# δεν μπορείς να κάνεις mock member που δεν είναι virtual. Αυτό σημαίνει ότι είτε μαρκάρεις όλα σου τα members virtual ή γράφεις interfaces.

 

--------------

 

Για να είμαι πιο ακριβής (αν και δεν ξέρω τι σημασία έχει), μπορείς να κάνεις mock non virtual members σε C# με διάφορα mocking frameworks, αλλά από όσο έχω δει είναι αρκετά πιο πολύπλοκο και υπάρχουν πολλές περιπτώσεις που δεν σου βγαίνει.

Επεξ/σία από kagelos
Δημοσ.

Ας πούμε έχεις ένα WindowControl object που είναι IDrawable, IScrollable, IDragable, IDisposable κλπ.

Eσύ φτιάχνεις μια συνάρτηση όπου μετράς πόσα objects έχουν γίνει dispose.

Κάποιος άλλος φτιάχνει αν πρόγραμμα ζωγραφικής και τον νοιάζει μόνο η Draw λειτουργικότητα.

Τα δύο Interfaces βοηθούν ώστε και οι δύο να φτιάξετε mock objects

Δημοσ.

Ας πούμε έχεις ένα WindowControl object που είναι IDrawable, IScrollable, IDragable, IDisposable κλπ.

Eσύ φτιάχνεις μια συνάρτηση όπου μετράς πόσα objects έχουν γίνει dispose.

Κάποιος άλλος φτιάχνει αν πρόγραμμα ζωγραφικής και τον νοιάζει μόνο η Draw λειτουργικότητα.

Τα δύο Interfaces βοηθούν ώστε και οι δύο να φτιάξετε mock objects

 

σε γενικές γραμμές έχω καταλάβει τι είναι τα Interfaces (δεν μου είναι εντελώς άγνωστα) , απλά μιλάω για projects που είναι γεμάτα "αχρείαστα?" Interfaces μήπως και γίνει κάποια προσθήκη στο μέλλον... δεν ξέρω... για αυτό ρωτάω για guidelines.

Δημοσ.

Για το dependency injection το καταλαβαίνω και συμφωνώ , για το unit testing τι δουλειά έχει?

 

Ιδανικά στα unit tests δεν εμπλέκεται κανένα concrete type εκτός από το system under test.

Δημοσ.

Ιδανικά στα unit tests δεν εμπλέκεται κανένα concrete type εκτός από το system under test.

 

Και αυτό αν έγραφες βιβλίο και το πέταγες έτσι αυτούσιο μέσα, θα υπήρχαν πολλοί που θα σου την έλεγαν, παρόλο που πάνω κάτω οποιοσδήποτε λογικός καταλαβαίνει τι θες να πεις.

 

Γιατί ιδανικό ως προς τι; Ως προς την ευκολία με την οποία θα μπορείς να κάνεις μια αλλαγή στο project; Και στο σενάριο που προσπαθείς να μη χρησιμοποιήσεις concrete classes, αλλά οδηγήσαι σε tests που χρειάζονται μπολικο maintain σε κάθε αλλαγή στο project; Εδώ πάει περίπατο η ευκολία συντήρησης.

 

Οπότε όλα αυτά είναι σχετικά και καθορίζονται από το που τα εφαρμόζεις.

 

 

σε γενικές γραμμές έχω καταλάβει τι είναι τα Interfaces (δεν μου είναι εντελώς άγνωστα) , απλά μιλάω για projects που είναι γεμάτα "αχρείαστα?" Interfaces μήπως και γίνει κάποια προσθήκη στο μέλλον...

 

YAGNI

  • Like 1
Δημοσ.

Γιατί ιδανικό ως προς τι; Ως προς την ευκολία με την οποία θα μπορείς να κάνεις μια αλλαγή στο project; Και στο σενάριο που προσπαθείς να μη χρησιμοποιήσεις concrete classes, αλλά οδηγήσαι σε tests που χρειάζονται μπολικο maintain σε κάθε αλλαγή στο project; Εδώ πάει περίπατο η ευκολία συντήρησης.

 

Ε σίγουρα οποιοσδήποτε τέτοιος "κανόνας" δε μπορεί να εφαρμόζεται παντού, δεν έχει νόημα.

 

Αυτό που εννοούσα είναι ότι έτσι δεν κουβαλάς κανένα implementation detail μέσα στο test, οπότε είναι πιο "pure" (και βασικά λιγότερο επιρρεπές σε breakage).

 

Γιατί όμως λες ότι χωρίς concrete classes χρειάζεσαι maintenance?

Δημοσ.

δεν έχω δουλέψει πολύ με Interfaces λόγω του ότι τα συναντάμε πιο συχνά σε μεγάλα projects, αλλά έχω δει μερικά projects και θεωρώ ότι γίνεται ανούσια χρήση των interfaces. του τύπου ρε παιδί μου να έχεις 15 classes και για τα 10 απο αυτα να εχεις και απο 1 Interface. και κανενα Interface να μην γινεται Implement σε πανω απο 1 κλαση.

 

κάποιο βιβλίο ή έστω online course για Interfaces best practices?

Μια κλάση έχει ήδη interface, είτε public είτε internal. Για αυτό δεν βγάζει νόημα να έχεις intrface. Για να βγάλεις νόημα θα πρέπει να δεις τι σε αναγκάζει να κάνεις impl ένα interface. Δηλαδή πρέπει να δεις τον caller.

Δημοσ.
Αυτό που εννοούσα είναι ότι έτσι δεν κουβαλάς κανένα implementation detail μέσα στο test, οπότε είναι πιο "pure" (και βασικά λιγότερο επιρρεπές σε breakage).

 

Γιατί όμως λες ότι χωρίς concrete classes χρειάζεσαι maintenance?

 

Γιατί δε θα χρησιμοποιήσεις mock objects; Όλες αυτές οι abstract classes/interfaces που θα χρησιμοποιήσεις θα πρέπει στα tests σου να έχουν κάποιο implementation (δημιουργημένο με οποιοδήποτε τρόπο) για να δημιουργήσεις mocks/fakes κλπ. Μπορεί από εντελώς κενό μέχρι πολύ περίπλοκο.

 

Όλα εξαρτώνται από το σκοπό που επιτελεί το τεστ σου. Η concrete class σου μπορεί να είναι κάτι πολύ περίπλοκο, με πολλές πτυχές ή μεταβαλλόμενες πτυχές, που στηρίζεται σε άλλα subsystems κλπ. Το δικό σου σύστημα που τεστάρεις μπορεί να είναι τέτοια η φύση του που το κάνεις update συνεχώς για να κάνει match αλλαγές του περιβάλλοντος/usage cases ή οτιδήποτε άλλο.

 

Οπότε δεν αποκλείεται το να σετάρεις μια concrete class και να βασιστείς στο implementation της που ήδη έχει subsystems για να δουλεύει παντού να είναι ο μόνος λογικός τρόπος ώστε να μη χρειάζεται να κάνεις ένα κάρο αλλαγές στα tests σου, σε κάθε αλλαγή της δικής σου κλάσης που τεστάρεις.

 

Το παραπάνω είναι φυσικά ένα υποθετικό σενάριο. Αν και κώδικας με τέτοιες ιδιαιτερότητες εύκολα μπορεί να βρεθεί όταν δημιουργείς multi-purpose software layers, σε embedded devices, σε κώδικα που πρόκειται να γίνει inject σε άλλο process κατά το runtime και μπορεί να μην έχεις κάνει καν πλήρως reverse τον κώδικα του target σου, αλλά τεστάρεις πάνω σε αυτόν και και και...

Δημοσ.

Οπότε δεν αποκλείεται το να σετάρεις μια concrete class και να βασιστείς στο implementation της που ήδη έχει subsystems για να δουλεύει παντού να είναι ο μόνος λογικός τρόπος ώστε να μη χρειάζεται να κάνεις ένα κάρο αλλαγές στα tests σου, σε κάθε αλλαγή της δικής σου κλάσης που τεστάρεις.

 

Αν καταλαβαίνω καλά τι λες, η concrete class είναι το SUT και τα subsystems είναι τα dependencies που θα δώσεις ως mocks ή οτιδήποτε άλλο. Στην περίπτωση αυτή πού βλέπεις πρόβλημα;

 

Γενικά επειδή το λες δεύτερη φορά, αν κάνεις αλλαγές στο SUT και αυτό σημαίνει ότι πρέπει να πας να αλλάξεις και τα τεστ, αυτό είναι ισχυρή ένδειξη πως το SUT and/or τα test είναι κακογραμμένα.

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...