Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I think the key here is what you <em>should</em> be testing.</p> <p>You've mentioned TDD in the tags, so I hope that we're trying to adhere to that here. By that paradigm, the tests you're writing have two purposes:</p> <ol> <li><p>To <strong>support your code</strong> once it is written, so you can refactor without fearing that you've broken something</p></li> <li><p>To <strong>guide us to a better way of designing components</strong> - writing the test first really forces you to think about what is necessary for solving the problem at hand.</p></li> </ol> <p>I know at first it looks like this question is about the first point, but really I think it's about the second. The problem you're having is that you've got concrete components you're testing instead of a contract.</p> <p>In code terms, that means that I think we should be testing interfaces instead of class methods, because otherwise we expose our test to a variety of problems associated with testing <strong>components</strong> instead of <strong>contracts</strong> - inheritance strategies, object construction, and here, renaming.</p> <p>It's true that interfaces names will change as well, but they'll be a lot more rigid than method names. What TDD gives us here isn't just a way to support change through a test harness - it provides the insight to realise we might be going about it the wrong way!</p> <p>Take for example the code block you gave:</p> <pre><code>[TestMethod] public void GetCity_TakesParidId_ReturnsParis(){...} { // some test logic here } </code></pre> <p>And let's say we're testing the method <strong>GetCity()</strong> on our object, <strong>CityObtainer</strong> - when did I set this object up? Why have I done so? If I realise <strong>GetMatchingCity()</strong> is a better name, then you have the problem outlined above!</p> <p>The solution I'm proposing is that we think about what this method really means <em>earlier</em> in the process, by use of interfaces:</p> <pre><code>public interface ICityObtainer { public City GetMatchingCity(); } </code></pre> <p>By writing in this "outside-in" style way, we're forced to think about what we want from the object a lot earlier in the process, and it becoming the focus should reduce its volatility. This doesn't eliminate your problem, but it may mitigate it somewhat (and, I think, it's a better approach anyway).</p> <p>Ideally, we go a step further, and we <em>don't even write any code</em> before starting the test:</p> <pre><code>[TestMethod] public void GetCity_TakesParId_ReturnsParis { ICityObtainer cityObtainer = new CityObtainer(); var result = cityObtainer.GetCity("paris"); Assert.That(result.Name, Is.EqualTo("paris"); } </code></pre> <p>This way, I can see what I really <em>want</em> from the component before I even start writing it - if <strong>GetCity()</strong> isn't really what I want, but rather <strong>GetCityByID()</strong>, it would become apparent a lot earlier in the process. As I said above, it isn't foolproof, but it might reduce the pain for this particular case a bit.</p> <p>Once you've gone through that, I feel that if you're changing the name of the method, it's because you're <em>changing the terms of the contract</em>, and that means you <strong>should</strong> have to go back and reconsider the test (since it's possible you didn't want to change it).</p> <p>(As a quick addendum, if we're writing a test with TDD in mind, then something is happening inside <strong>GetCity()</strong> that has a significant amount of logic going on. Thinking about the test as being to a contract helps us to separate the intention from the implementation - the test will stay valid <em>no matter what we change behind the interface</em>!)</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload