Reputation: 392
This class is fairly simple, it adds a twitter hashtag to a string if there is room for it. Twitter only allows 140 characters (minus 23 for a url). So the hashtags keep getting added if there is space for one.
I don't think it's 100% working as expected, but that is not relevant to my question which is located below.
class Hashtags {
private $url_character_count = 23;
private $characters_allowed = 140;
public function __construct(Article $article)
{
$this->article = $article;
$this->characters_remaining = $this->characters_allowed - $this->url_character_count;
}
public function createHashtagString()
{
$hashtags = '';
$hashtags .= $this->addEachNodeHashtag();
$hashtags .= $this->addHashtagIfSpace($this->article->topic_hashtag);
$hashtags .= $this->addHashtagIfSpace($this->article->pubissue_hashtag);
$hashtags .= $this->addHashtagIfSpace($this->article->subject_area_hashtag);
$hashtags .= $this->addHashtagIfSpace('#aviation');
return $hashtags;
}
private function addEachNodeHashtag()
{
//Returns a hashtag or calls hashtagString() if it is a comma separated list
}
private function hashtagString()
{
//Explodes a comma seperated string of hashtags and calls addHashtagIfSpace()
}
private function addHashtagIfSpace($hashtag_string)
{
if((strlen($hashtag_string) + 1) <= $this->characters_remaining)
{
$this->characters_remaining = $this->characters_remaining - strlen($hashtag_string);
if(empty($hashtag_string))
{
return '';
}
return ' ' . $hashtag_string;
}
}
}
Here is my test, my problem is that this only tests one specific case, where all the fields are filled in, and when there is enough space to fit them all. Should I just keep making a bunch of these test functions for different cases? I am guessing there will be about 10 of them. I have never done testing before, so I am a bit out of my element and need to to pointed in the correct direction.
Thank you
class HashtagsSpec extends ObjectBehavior
{
function it_creates_hashtag_string_with_all_fields_filled_in(Article $article)
{
$this->beConstructedWith($article);
$article->title = 'This is the article title';
$article->url = 'http://website.com/node/XXX';
$article->pubissue_hashtag = '#AHASHTAG';
$article->subject_area_hashtag = '#SUBAREA';
$article->topic_hashtag = '#TOPIC';
$article->node_hashtags = '#Test1,#Test2,#Test3';
$this->createHashtagString()->shouldReturn(' #Test1 #Test2 #Test3 #TOPIC #AHASHTAG #SUBAREA #aviation');
}
}
Upvotes: 2
Views: 661
Reputation: 36191
Step 0
Remove your class and start over by writing specs first.
When doing this you'll often find yourself writing a different (simpler) implementation, when driving it with specs. It won't take much time, but your class will benefit of a better design and testability.
I often use this practice when I don't know what code should look like. I prototype it first. Once the design starts to clarify, I remove the code and start over by speccing it.
You don't have to remove it for real, make a backup ;)
Step 1
Define your initial test list. This will be a list of behaviours you think you need to cover. It doesn't have to be complete and it will evolve as you go along.
You could start with:
Step 2
Write a first spec. Think of better naming as Hashtags might not be specific enough. Also consider a better API for your class. I chose to accept Article in a method call rather than passing it via the constructor:
class HashtagsSpec
{
function it_adds_a_topic_hashtag_if_there_is_room_for_it_in_the_message(Article $article)
{
$article->pubissue_hashtag = '#AHASHTAG';
$article->subject_area_hashtag = '#SUBAREA';
$article->topic_hashtag = '#TOPIC';
$article->node_hashtags = '#Test1,#Test2,#Test3';
$this->createHashtagString($article)->shouldMatch('/#TOPIC/');
}
}
Step 3
Run phpspec and write the simplest code to make specs pass.
class Hashtags
{
public function createHashtagString(Article $article)
{
return $article->topic_hashtag;
}
}
Step 4
Refactor - improve the design of code you wrote in Step 3.
It might be that there's nothing to improve, especially in the first iteration(s).
As you go along, your code will become more generic, while your specs become more specific.
Step 5
Repeat steps 2 to 5 until you're done. Simply pickup next behaviour you want to cover. It doesn't have to be the next one on your list. Whatever you feel is best to implement next.
During the whole process you'll often discover new behaviours or edge cases you haven't thought about before. Simply add them to your test list so it doesn't distract your flow.
Upvotes: 26