lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] On the morality of using const_cast in unit tests


From: Vadim Zeitlin
Subject: Re: [lmi] On the morality of using const_cast in unit tests
Date: Wed, 3 Aug 2016 19:08:47 +0200

On Wed, 3 Aug 2016 16:18:16 +0000 Greg Chicares <address@hidden> wrote:

GC> Vadim--Is it a code smell to use const_cast in unit tests?

 In general I'd say that using const_cast<> in order to really modify an
object via it (and not just to pass a pointer to the legacy and not
const-correct API) is indeed suspicious.

GC> Motivating case: commit fe56ee89d923767307228921f9b3a11c5f6769e1,
GC> abbreviated and rearranged for clarity here:
GC> 
GC>      database_entity const scalar(DB_PremTaxLoad, 0.0000);
GC> -    DBDictionary::instance().datum("PremTaxLoad") = scalar;
GC> +    DBDictionary& dictionary = const_cast<DBDictionary&>(*db.db_);
GC> +    dictionary.datum("PremTaxLoad") = scalar;
GC> 
GC> The unit test sets up this "what-if" circumstance in order to validate
GC> class premium_tax's operation.

 I think the way I'd do this would be to provide a way to test such what-if
scenario in product_database itself by allowing to replace its "db_"
member. Of course, these methods would need to be private (if we keep
premium_tax_test as a friend of this class) or be explicitly marked as 
being for testing only. But if we had them, then, instead of using a const
case, we could just make a copy of the original DBDictionary and then
create a product_database representing it:

        DBDictionary dictionary = db.get_db_dictionary();
        dictionary.datum("PremTaxLoad") = scalar;
        product_database dbForTest
                (dictionary
                ,... all the usual parameters or, maybe, yare_input
                );

I think this is preferable not only because it avoids the const_cast but
also because it avoids temporary modification to the original "db" object
and hence we don't have to worry about undoing it later. IMHO it's also
more clear.

GC> The original (prior to this month) cache held only one such container,
GC> and was non-const. The new caching implementation holds any number of
GC> such containers, and its API provides only const access (i.e., through
GC> a shared_ptr<T const>). This const API seemed like a good idea because
GC> production code should never change any cached database; but unit tests
GC> do have good reason to change it.

 FWIW I definitely agree that it makes sense for the API to be const.

GC> It might be better to use shared_ptr<T> instead, both in the caching
GC> templates, and in class product_database (which holds it as a private
GC> member and dereferences it only in a single const function anyway).
GC> Then friendship lets unit tests access the private (non-const) member
GC> without const_cast. What's your opinion?

 Originally I thought it would be a pity to lose constness guarantee
inside product_database code but after seeing that it's indeed only used in
a single place, this doesn't look like a big deal any more, so I agree that
it would be a slightly better alternative to the current code. But I'd
still prefer to keep the constness and just provide a way to inject a
modified DBDictionary into product_database for testing purposes as
described above.

 Regards,
VZ


reply via email to

[Prev in Thread] Current Thread [Next in Thread]