<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Ted Cheng on Medium]]></title>
        <description><![CDATA[Stories by Ted Cheng on Medium]]></description>
        <link>https://medium.com/@tedchength?source=rss-6f4cf106090e------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Sdc9OkUU-oir3Qqi3t-ZpQ.jpeg</url>
            <title>Stories by Ted Cheng on Medium</title>
            <link>https://medium.com/@tedchength?source=rss-6f4cf106090e------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 17:31:43 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@tedchength/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Management for Agile development]]></title>
            <link>https://medium.com/@tedchength/management-for-agile-development-d5e9e9efe6d6?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/d5e9e9efe6d6</guid>
            <category><![CDATA[agile-development]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Mon, 11 May 2020 17:46:36 GMT</pubDate>
            <atom:updated>2020-05-11T17:46:36.755Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oZPJIEtVUHWoRvGNihQjdQ.png" /><figcaption>Splitting Story and Task</figcaption></figure><p>I assume you already know something about agile development before you start reading this article. In the simplest way, “MOVE FAST”. Things are always easy to tell and understand, but, most of the time, difficult to execute. In this article, I am going to share how my team is working and how I clarify these things (after a huge failure, ha, anyway).</p><h3><strong>Problem</strong></h3><p>There are lots of ways to make you feel the whole work flow stuck. We are not going to discuss it one by one. Let’s just directly talk about the symptoms.</p><pre>1. Cards cannot move / deploy for days<br>2. Cannot scale up development rate easily<br>3. Code pull requests stay alive for too long<br>4. (name yours)</pre><p>To all these questions, people always say “JUST BREAK IT DOWN”.</p><p>“I did try it. It just didn’t work out”, you may say.</p><h3><strong>Thoughts</strong></h3><p>To me, there are two key factors of “BREAK IT DOWN”.</p><pre>1. Small User Stories <br>2. Spliting Story Ticket and Task Ticket</pre><h3><strong>Small User Stories</strong></h3><p><em>Case 1</em></p><p>ONE ticket should only contain ONE user story. Actually, it’s still a vague definition. To be frank, before our whole work flow revision, I received a story ticket from the product team.</p><pre>FEATURE - resource listing page<br>  1. clicking the create button will go to edit page <br>  2. clicking the delete button in drop down will deactivate the script<br>  3. ... more</pre><p>Obviously, this ticket is too big and includes lots of user stories. A better one will be</p><pre>FEATURE - In listing page, clicking the create button will go to edit page<br>FEATURE - In listing page, clicking the delete button in drop down will deactivate the script</pre><p>Easy, right ?</p><p><em>Case 2</em></p><p>Sometimes, we may also need to break down ONE user story into multiple story tickets, especially, when you are building a whole new things.</p><p>For example, we are working hard to build a new epic feature. Whole new frontend pages. Whole new backend structure. We made a mistake in our planning.</p><p>Our ultimate goal is to build a flow that can be triggered by keywords and give some response. We had simplified our stories.</p><pre>1. FEATURE - User can setup keywords for the response<br>2. FEATURE - User can setup more for further response</pre><p>We thought it made sense already. However, it actually didn’t. Setting up keywords and setting up response both already requires lots of data structure and new UI components since the project just start. Some of the components are required to be library standard components. It’s like building a Titanic already.</p><p>In the end, we setup straw men for a better story management.</p><pre>1. FEATURE - User can setup keywords for a dummy response <br>2. FEATURE - User can customised the response </pre><pre>//we should even do more if we know things earlier...<br>3. FEATURE - User should see a keyword setup field in edit page<br>4. FEATURE - User should see the keyword in edit page after saving</pre><p>A small conclusion here.</p><pre>1. A ticket should always describe only one behaviour<br>2. Setting up a straw man for further breaking down the story<br>3. A story ticket&#39;s goal need not to be a complete use case</pre><h3>Splitting Story ticket and Task ticket</h3><p>The second part is hard to swallow. Please be careful.</p><ol><li><strong>Story ticket</strong> is ONLY responsible for user story management. It concerns only the product team and the qa team. It mainly passes through phases like use case review and functional testing</li><li><strong>Task ticket</strong> is ONLY responsible for application behaviour. It concerns only the dev team . It mainly pass through phases like developing and code review . It has its own target. Once it meets, it’s done. Here we are not talking about code changes but the application behaviour. The reason behind is another story. The main purpose is to reduce mistakes during your planning</li><li>The relationship between is <strong>Story ticket</strong> is blocked by <strong>Task tickets. </strong>The application behaviours in task tickets are supposed to complete the user story in <strong>Story ticket .</strong></li><li>This splitting separates the life cycle of Story and Task. It makes the life cycle shorter and easier to manage. The time estimation can also be split.</li><li>A task ticket marked in build means the application behaviour will be there after the deployment.</li><li>A task ticket marked in build means the user story will be there after the deployment</li><li>Build testing should ensure expected behaviours in the build. Say, if Story A consist of Task B, C ,D. If only B is done and in build, the testing should make sure A doesn’t exist in the build and B doesn’t affect any expected behaviours</li><li>Code review should go before use case review and functional testing as we defined expected behavior in the <strong>Task ticket</strong>. The order of importance should be structural conrrectness &gt; use case correctness</li><li>Reviewed PR can be merged directly. It avoids the problems of feature branch strategy.</li><li>If the finished work totally don’t fulfil the use case, we should review our communication process and also the planning instead</li><li>Cards are managing works but not code. PR should not be a must for a story ticket.</li></ol><p><strong>Operations</strong></p><p><em>Normal flow</em></p><ol><li>Product manager create a story ticket</li><li>Engineers estimate related application behaviour it should have to complete the use case and create related task tickets</li><li>Engineers complete a task and ask for review.</li><li>If it’s okay, merge the PR and put it into a build (mark in build )</li><li>Engineers complete those task tickets and do an integration. If things are good, ping Product manager for Use case review and QA for functional testing. update the story card to use case review phase</li><li>If the user story cannot be completed by the task tickets, we can consider add more task ticket until we complete the use case.</li><li>When <strong>task ticket </strong>is done, put it in a build and so is the <strong>story ticket</strong></li></ol><p><strong>Kinds of story ticket</strong></p><ol><li>new user story</li><li>issue fix. it’s also a kind of user story change.</li></ol><p><strong>Kinds of task ticket</strong></p><ol><li>Backend new structure basic, like, new model or new service. The card will only provide the structure and, most of the time, provides no logic to handle use case</li><li>Backend behaviour change, like, validation rules change. if no code change, an integration test is still necessary as a prove</li><li>Backend API change. if no code change, an integration test is still necessary as a prove</li><li>Frontend shared library component</li><li>Frontend integrating the library component into an application as an application component</li><li>Frontend saga and reducer for an action.</li><li>Frontend new feature component</li><li>Integration</li></ol><p>We do break down our task tickets into very small pieces. It benefits a lot.</p><ol><li>small task means small PR. much easier to review.</li><li>small task give a very precise goal to each ticket</li><li>it ensure we plan before we work</li><li>tickets move fast</li><li>easy to track the progress of work</li><li>A user story can be highly distributed to engineers, not just divided by frontend and backend.</li><li>highly distributable work allow us to push a user story faster.</li><li>Team can work on stories one by one. Otherwise, review and testing can only be started just before sprint end</li></ol><h3><strong>Conclusion</strong></h3><p>There are still lots of details in the flow, but the key concepts are here. We should also ensure code reviewing is the highest priority task for engineers. Every PR shouldn&#39;t stay for more than one day. For those stay, we also need to revise our planning process of tasks.</p><p>No solution can handle all situation. As a team lead, we should always review our work process to ensure things go smoothly and efficiently. And more importantly, it’s our responsibility to make everyone works happily.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d5e9e9efe6d6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unit Testing — HOW method and WHAT method]]></title>
            <link>https://medium.com/@tedchength/unit-testing-how-method-and-what-method-9929972e37c?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/9929972e37c</guid>
            <category><![CDATA[good-practices]]></category>
            <category><![CDATA[unit-testing]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[coding]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Sat, 06 Oct 2018 11:43:38 GMT</pubDate>
            <atom:updated>2018-10-06T11:43:38.476Z</atom:updated>
            <content:encoded><![CDATA[<h3>Unit Testing — HOW method and WHAT method</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*53NJDdC8_ipX2ZC1g-DDBw.png" /></figure><p><strong>Caution: This is a very subjective article.</strong></p><p>Before reading this article, you may want to know more about HOW method and WHAT method.</p><p><a href="https://medium.com/@tedchength/coding-how-method-and-what-method-d272067fef0">https://medium.com/@tedchength/coding-how-method-and-what-method-d272067fef0</a></p><p>Unit testing is one of the important development steps. Every method in you program should be well unit-tested. When talking about unit testing, people always talk about high code coverage. Yet I do think we should also consider case coverage. Case coverage is even more important than code coverage. Having high code coverage and high case coverage, you just need to add a few more integration test.</p><p>However, some may say that having too many test cases is a kind of trouble. Every time you make changes, fixes and refactors, you need to update a lot of test cases. Even when you just finish a method, you may also find your new method is difficult to test. Unfortunately, you need to do a refactor and update your test cases.</p><p>But can we avoid this? I would like to share my experience. It’s also based on the concept ofHOW and WHAT .</p><p>Let’s think about a feature.</p><pre>Feature<br>As an programmer, I order bubble tea under some conditions so that I can recharge myself</pre><pre>Given today is Thursday<br>When I think of bubble tea<br>Then I would make no order</pre><pre>Given I am in a hyper mood<br>When I think of bubble tea<br>Then I would make order by myself</pre><pre>blah... blah... blah...</pre><p>And I try to make a class (not in a detail way)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1016/1*offZWImYhMcFGqgi7JWhlQ.png" /></figure><p>Considering the orderBubbleTea method, there are lots of factors altering the return value. The return value changes under different situations and combinations of these factors.</p><pre>factors: [<br>  &#39;day&#39;, //line 4,<br>  &#39;emotion&#39;, //line 6, 14<br>  &#39;favoriteDrink&#39;, //line 8<br>  &#39;tryNewDrink&#39;, //line 10<br>  &#39;returnOfMakePhoneCall&#39;, //line 15<br>  &#39;returnOfMakeOrder&#39;, //line 16<br>  &#39;returnOfAskOthersToMakeOrders&#39;, //line 21<br>]</pre><pre>factorTypes: {<br>  day: [&#39;thurs&#39;, &#39;other&#39;],<br>  emotion: [&#39;normal&#39;, &#39;hyper&#39;, &#39;other&#39;],<br>  favoriteDrink: [&#39;normal&#39;, &#39;null&#39;],<br>  tryNewDrink: [&#39;???&#39;],<br>  returnOfMakePhoneCall: [&#39;withMakeOrderMethod&#39;, &#39;withoutMakeOrderMethod&#39;],<br>  returnOfMakeOrder: [&#39;anything&#39;],<br>  returnOfAskOthersToMakeOrders: [&#39;anything&#39;],<br>}</pre><p>(I am not going to describe every assertion for the test. Just some points to note is that, for testing line 16-18 and line 21-23, we better assert the result should be what the method call (makeOrders, askOthersToMakeOrders) return with some mocking and faking. And for line 15-16, we can just assume, according to the code base, we can only handle cases that `makePhoneCall` will return a object with `makeOrders` interface. )</p><p>You may find there are too many factors to test and the tryNewDrink logic is too difficult to test. So, we do a refactor.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/1*4L1GBCUCt5HjeFaDyV_xGg.png" /></figure><p>Obviously, the methods are easy to test now. They are now with limited factors and factor types. But this is not exactly what we want to do… We want to avoid this kind of refactor.</p><h3>Lesson to learn</h3><p>We better have a good plan before we code. Consider the above example.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1016/1*lt5rucG5FuVaMs6mh8137Q.png" /><figcaption>before refactoring</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fStVgdikWAr5YwwLLw-dMA.png" /><figcaption>after refactoring</figcaption></figure><p>What do you realize? Do you know what green part and red part represent ? They are actually HOW and What .</p><blockquote>For those methods mixing HOW and WHAT , they are always difficult to test. If we can write our methods using the HOW and WHAT principle, clearly we can make our lives easier.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wjMvsM3iJItYe1d8601RRw.png" /></figure><p>This is what I summarize for testing HOW method and WHAT method .</p><p>If you remember, a HOW method usually does calculations, algorithms and service calling. In this kind of cases, you want to test if the input and output are exactly match. For an easiest example, for a (a, b) =&gt; a + b , you may want to test with different sets of input and output to ensure the calculation is correct. (though we may need some other treatments to test those service calling method)</p><p>On the other hand, a WHAT method usually controls the flow . We need to test if methods are called with certain number of times or with that payload / if methods return that object returned by that method call. Lots of mocking and faking are required. We don’t care how that thing is, but that thing must be that thing. You can say it’s about the reference pointer.</p><h3>Summary</h3><p>If you have read these two articles, you can see the benefit of doing HOW and WHAT . It benefits our structuring, testing and planing. Case coverage should always be your big concern of testing. I created my tools to increase my test case coverage. You can check the link below. Actually this tool is deprecated, I created one rely on jest , but you can still have a look. You can find how I structure my test case to reduce the workload even I write a high case coverage test case.</p><p><a href="https://www.npmjs.com/package/mocha-combo">https://www.npmjs.com/package/mocha-combo</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9929972e37c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Coding — HOW method and WHAT method]]></title>
            <link>https://medium.com/@tedchength/coding-how-method-and-what-method-d272067fef0?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/d272067fef0</guid>
            <category><![CDATA[coding-style]]></category>
            <category><![CDATA[best-practices]]></category>
            <category><![CDATA[thinking]]></category>
            <category><![CDATA[coding]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Fri, 05 Oct 2018 16:50:41 GMT</pubDate>
            <atom:updated>2018-10-06T05:31:43.895Z</atom:updated>
            <content:encoded><![CDATA[<h3>Coding — HOW method and WHAT method</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vWrgaq4cxNBWDDFT5q2-cw.jpeg" /></figure><p><strong>Caution: This is a very subjective article.</strong></p><p>I guess most of the programmers should have heard about Single Responsibility Principle . Yet, it may be a little bit abstract. That’s why I would like to simplify this with HOW method and WHAT method . Before moving on, please bare in mind that</p><blockquote>HOW and WHAT are just a relative description. There is no fixed line to separate them.</blockquote><h3>HOW method</h3><p>A HOW method is a method that describe HOW TO DO to archive a target. For example,</p><pre>//example 1<br>function add(a, b) {<br>  return a + b<br>}</pre><pre>//example 2<br>function fibonacci(num) {  <br>  if (num &lt;= 1) return 1;   <br>  return fibonacci(num - 1) + fibonacci(num - 2);<br>}</pre><pre>//example 3: transforming data structure<br>function transform(obj) {<br>  Object.keys(obj).reduce( (key) =&gt; {<br>    //some logic here<br>    return values.forEach( (value) =&gt; {<br>       //some logic here<br>    })<br>  }, {})<br>}</pre><p>This kind of methods is more focusing on algorithms and calculations. They are the most fundamental components of the whole program. You may find them having operators and aggregating method calls. Besides, they may also call services , like connecting Database, calling AWS S3.</p><h3>WHAT method</h3><p>A WHAT method is a method that describe WHAT TO DO to archive a target. For example,</p><pre>function orderPizza() {</pre><pre>  const whatToEat = pickFromMenu()<br>  const shop = makeCall(&#39;12345678&#39;)</pre><pre>  if (!shop) return null<br>  return shop.makeOrder(whatToEat)</pre><pre>}</pre><p>This kind of methods is more focusing on flow and what steps to make to complete the task. They connect fundamental building blocks. You can find them heavily rely on calling other methods and calling iterating methods .</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EzUxIGTlS-uV8PkmcxFGFw.jpeg" /><figcaption>A good structure</figcaption></figure><p>If you really understand the above things, you will realize the simplest example of how HOW and WHAT work together is the relationship between Controller and Model .</p><h3>Controller and Model</h3><p>Controller and Model are always only recognized as the components of MVC framework. Actually, simply by their naming, you should easily find that they are actually HOWand WHAT .</p><pre>class UserController {</pre><pre>  //POST /users<br>  create(req, res) {<br>    try {<br>      const user = User.create()<br>      return user<br>    }catch(e) {<br>      return new ErrorResponse(e, 400)<br>    }<br>  }</pre><pre>}</pre><pre>class User {<br>  create() {<br>    //connect DB<br>    //insert record<br>    //return user or throw error<br>  }<br>}</pre><p>The controller describes WHAT TO DO to handle the POST request. The model describes HOW TO DO to create a user record.</p><h3>Delegate Pattern and Hooks</h3><p>Understanding writing flow helps you make good use of Delegate Pattern and Hook.</p><p><strong>ReactComponent</strong></p><p>ReactComponent provides methods to fill. For example,</p><ol><li>render() //return elements to display</li><li>componentDidMount() //extra things to do when the event happen</li></ol><p>You don&#39;t need to implement when the render and componentDidMount to be called. You just need to manage these specific phases. If you think deeply, it&#39;s just the author of ReactComponent control the flow and WHAT TO DO of the whole DOM tree rendering flow.</p><p><strong>iOS UITableView</strong></p><pre>UITableView *tableView = new UITableView()<br>tableView.datasource = (UITableViewDataSource *)datasource<br>tableView.delegate = (UITableViewDelegate *)delegate</pre><pre>//UITableViewDataSource interface<a href="https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614860-numberofsectionsintableview?language=objc"><br>numberOfSectionsInTableView:</a><br><a href="https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614931-tableview?language=objc">tableView:numberOfRowsInSection:</a></pre><p>This is another example. The UITableView works with the UITableViewDataSource. They control the flow. Programmers fill in the HOW TO DO .</p><p>There are also lots of other examples, like the Directive of Angular.js, UIViewController of Objective-C, those Before/After hooks of those testing framework.</p><h3><strong>My experience</strong></h3><p>I built a messaging center for my team. It provides features like sending notifications and broadcasting messages. The messages should also reach multiple platforms.</p><p>I wrote a Class called MessageRequest which manage the whole flow of sending a message. It gathers information like the channels, endpoints of getting the list of the target audiences and the subscriptions of them. Yet, this class only control the flow. It never determine how to get the channel and subscriptions. These all are left to the delegates. So now, every new use case of sending messages, our engineers just need to do a fill-in-the-blanks. Since all these similar use cases are conforming to the same structure, duplicated code can be easily solved by subclassing or a deeper delegate patterns. Besides, due to the same reason, all test cases look very alike. In our implementation planning, we can easily foresee the whole outcome.</p><h3>Summary</h3><p>Understanding the above concepts help you to build a better code structure. I always use this methodology to build flows. Similar features can make use of the same flow. It ensures that you will always have ONE SOLUTION TO ONE QUESTION . Your implementation planning, test case writing and code reviewing will be way lot easier.</p><p>In next article, I will talk about how this HOW AND WHAT affect your unit testing. I guess it will give you a new insight. Thanks :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d272067fef0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Web Automated Testing in practice]]></title>
            <link>https://medium.com/@tedchength/web-automated-testing-in-practice-4af436333ba3?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/4af436333ba3</guid>
            <category><![CDATA[selenium]]></category>
            <category><![CDATA[automated-testing]]></category>
            <category><![CDATA[cucumber]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[software-testing]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Sun, 04 Feb 2018 04:53:32 GMT</pubDate>
            <atom:updated>2018-02-04T04:53:32.849Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p40Qo89p4aAD2CZEdcrA8g.png" /></figure><p>I am going to talk about web automated testing. If you read the articles I wrote before, you would find that they were all about theory or even they were just experiment. Luckily, in the previous a quarter of a year, I have the chance to practice the whole thing.</p><p><strong>“Once upon a time…”</strong></p><p>The background of the whole was very common. A company did its QA testing manually and tried to make it automated. Nothing more. The result wasn’t that good. It was not scalable. It took lots of time to run once. Its learning curse was steep.</p><p><strong>“Now…”</strong></p><p>We have ways to aim at a 15-minute testing. (Actually, it can be faster depends on what you need. We will talk about the bottlenecks we have). And the whole things is easy to learn. Teammates can join the test development just after a half-day training.</p><p>Though there are still lots of problems to solve, as long as it’s scalable, it can be used in production, I think it’s already a kind of success.</p><p><strong>If you are going to do W.A.T</strong></p><p>The first thing you need to do is to let your boss know it’s never an easy to task to do. It involves different parties like devops, QAs and engineers (even better if you also get the product team). What I saw in different companies was it’s so hard to get these parties to work together. They have their own schedules. Probably they won’t want to invest a side-project with no guaranteed outcome.</p><h3><strong>My journey - How I start</strong></h3><p><strong>the 1st Step</strong></p><p>My first step wasn’t anything special. I just did a research on it. I picked Selenium, Docker, cucumber and Javascript. And in the end, I found I couldn’t skip Protractor and Jenkins.</p><ol><li>Selenium — it’s the most common tool. It provide HTTP for you to work with. That’s mean there is no limitation on how you do your coding. It also provides an easy scaling solution. That’s the Selenium Grid.</li></ol><p>2. Docker — the MVP of your whole setup, especially you are not a devops guy. How to install Selenium ? Docker. How to provide an easy setup development environment for QA? Docker. Docker, Docker and Docker. It helps a lot.</p><p>3. Cucumber — it is a framework that you can write Gherkin file (BDD). Some people will pick Jasmine, mocha or the others. Yet I do think mocha and Jasmine are too engineer-oriented about its syntax and structure. It’s so easy to foresee that most of the tests will be developed by QA. And also the power of a Gherkin file is far more than you can expect. I’m going to talk about this later.</p><p>4. Javascript — picking javascript is simple because it’s the most common programming language. Backend and Frontend developer both knows it. Programming newbies will also learn javascript first. (And I am a nodejs developer… ha)</p><p>5. Protractor — I had a misunderstanding about cucumber and protractor before. Eventually, I found that they are two layer things. The main purpose of using Protrctor is to do parallel test running. Protractor also claim that it support Angualar testing and also provides its own webdriver. I didn’t spend to much time on this. I just use it.</p><p>6. Jenkins — I just use it to link up the shell scripts. so we can just do things by clicking. Also, making Jenkins a starting point of test will make you test more stable and faster.</p><p><strong>the 2nd Step</strong></p><p>The second step was doing my own framework. I am not going into details.</p><ol><li>some shell scripts — it makes difficult command easy</li><li>docker container of nodejs — teammates only need to install docker</li><li>docker-compose for a selenium hub — teammates can run their own server easily</li><li>Page object structure — Actually it’s a very common structure if you are using cucumber</li><li>HTML reporting.</li></ol><p>6. Test Logs — logs are very important. Otherwise, test developers will need to see the test steps running in browsers for debugging</p><p><strong>the 3rd step</strong></p><p>Next, I did a “pitch deck”. I knew I needed to persuade different parties, including engineers, QAs, the product managers and, of course, my boss, to &quot;invest&quot; my new &quot;product&quot;. I made sure my deck was easy to understand. Less words, more visual graphics. More equations of benefits, less imagination.</p><p>Of course, I couldn&#39;t make them all onto the boat. Yet at least we could start sailing.</p><h3>My journey - crews</h3><p>I separated work by 3 roles:</p><ol><li>Engineer — handle all the coding stuff. provide new features of the framework. do code review.</li><li>Quality assurance engineer (QE)— do simple coding. make Gherkin file runnable test cases. run automated test for deployment</li><li>Quality assurance(QA) — write Gherkin file.</li></ol><p>At this moment, I guess the best ratio of roles is 1:4:6. Yet I still don’t have a chance to give it a try. The key part is having good QEs. They know what the test cases are about. They know how to debug. They connect the programming things and the non-programming things. It’s also possible replacing QEs with Engineers, but it’s not cost-effective.</p><h3>My journey — problems</h3><p><strong>Scaling the selenium server</strong></p><p>The loading of the whole selenium grid should be measured accurately. Otherwise, you will get lots of timeouts during your testing. And many things will also cause you a timeout. It will be hard to debug, so I would suggest you really do a load test to ensure your gird provide enough resources.</p><p>Making use of docker, I was able to scale out the whole selenium server easily. Seleinum provides different kinds of Dockerfile. You can get the Selenium hub, the selenium node with chrome or other browsers. I just wrote a docker-compose file to manage the structure. After reading a few tutorials, my temporary setup is like this.</p><ol><li>one C4.xlarge. It hosts the selenium hub and the Jenkins.</li><li>one C4.2xlarge. It hosts the selenium node with chrome which provide 30 sessions.</li></ol><p>Remarks after some experiment:</p><ol><li>selenium node consume quite a lot of memories.</li><li>selenium hub consume just a few memories and also CPU. You just need to ensure that’s enough network bandwidth</li><li>there are two factors of scaling. one is the number of instances of each selenium node. one is the number of selenium node. Some say you cannot just scale one of it. You need to find the magic balance of the scaling. Yet I have no efficient way to test it. I just scale the number of instances. (Since I use a large aws instance for the nodes. More nodes in the instance means more ports to open.)</li><li>I found that one c4.xlarge aws instance can support one selenium node with 8 sessions.</li><li>I found that my C4.xlarge only used about 3GB memory and under 5% CPU usage. (that’s why I also put the Jenkins in it)</li><li>Jenkins is a must if you really want to shorten your testing time. I tried to use my computer directly sending requests to the selenium grid in aws. There were lots of timeout when I scaled to a certain level.</li><li>Make sure you understand the maxInstance of protractor, max instance of selenium node and max sessions of selenium node. They are just like some gates. Every of them can be the bottleneck. What I found is the maxInstance of protractor means how many threads you want to start. max instance and max sessions of selenium node mean how many threads you want them to receive.</li><li>I am sure we will need another infra-structure for auto-scaling of selenium node. The current structure just fits our need.</li></ol><p><strong>Fixture setup</strong></p><p>If you manage your fixture well, it will shorten your test time a lot.</p><p>Many of your test cases are going to create, read, update and delete your records. You can foresee that if your doing a parallel run with the same account, things will be very messy, even make test fail. There are also different kinds of accounts to setup. Some with these records inside, some with those records. That why we need lots of accounts. More importantly, you need a way to make sure you test case get the correct accounts.</p><p>I would like to split the question like this:</p><ol><li>how to create test account efficiently</li></ol><p>What we did is just create a “test case” including all the step for setup up an account. Of course we also some script to reduce the extra operations.</p><p>2. how to route the correct and available account to the test case</p><p>I wrote a ResourceManager class in my framework. It can will mark the account in use by writing a .tmp file and remove it when it become available again. Also, we have a config file storing the account with tags. By matching with the tag of each test case, the ResourceManager is able find a suitable account for the cases</p><p>Actually the ResourceManager is able to handle things other than accounts. You just need to give it a certain structure of data with tags. It will help you to manage it.</p><p>3. how to clean up the account after test</p><p>I added some roll back steps for each test case. If this test will create record A, I delete it in the roll back steps. I had read some articles. People are doing the same.</p><p>There are sill lots of minor things you need to handle. See if can write another short article on it.</p><p><strong>Scaling test development</strong></p><p>We really did try to add engineers to scale out the QE workers. The result was good enough. They just focused on handling the simple coding and verified the test case by some basic sense. If there were questions about the correctness of the test cases, they just ask the QA. But really, it’s not cost-effective.</p><p><strong>Test deployment</strong></p><p>It’s not good if you just rely on the automated test cases just after they were finish. Automated test is also a problem. It will go wrong. That’s why we adopted the normal development flow for application. We have master branch, staging branch and development branch.</p><p>The master is used for testing the application deployment. The staging branch is used for next release of test cases. We just make sure that the master branch and staging are always all passed.</p><p>We release new test cases every week. For every test case we release, we will still do the manual testing for almost 2 weeks. It’s just a simple double-entry method. After this period, we just rely on the automated test. You can also change criteria to “automated and manual test for 5 times” depends on what you need.</p><h3><strong>Summary</strong></h3><p>This article is just a brief summary. How the framework is like? How to really start a selenium grid with proper connections? Maybe we will also have other big problem later on. Just comments and leave questions. I will keep sharing my experience in this jounary.</p><p>You may like to read my old article about how I think about Docker, selenium and cucumber:<br><a href="https://medium.com/@tedchength/web-automation-test-solutions-with-docker-selenium-and-cucumber-js-d01482786e6c">https://medium.com/@tedchength/web-automation-test-solutions-with-docker-selenium-and-cucumber-js-d01482786e6c</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4af436333ba3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Web Automation Test Solutions with Docker, Selenium and Cucumber.js]]></title>
            <link>https://medium.com/@tedchength/web-automation-test-solutions-with-docker-selenium-and-cucumber-js-d01482786e6c?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/d01482786e6c</guid>
            <category><![CDATA[automation-testing]]></category>
            <category><![CDATA[selenium]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Fri, 25 Aug 2017 16:27:28 GMT</pubDate>
            <atom:updated>2017-08-25T16:58:32.309Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zZ0qgZDGjWvHKkfr5sL1pw.jpeg" /></figure><p>Objective: build a feasible web automation testing solutions that lower the learning curve of the whole setting up and coding.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DI78KAQAprgzciQRnuT7vw.jpeg" /></figure><p><strong><em>Selenium Grid</em></strong></p><p>Selenium helps you communicate and get elements from browsers. It receives HTTP requests and responses with the data you need. You can foresee that every programming language, that is able to send HTTP request, can communicate with Selenium</p><p>A Selenium grid consists of hubs and nodes. Hubs distribute works to nodes. Nodes receive orders and communicate with browsers. Obviously, it’s an architecture that you can do the scaling out easily.</p><p><strong><em>node.js</em></strong></p><p>Javascript is now a widely used language. AngularJS, ReactJS and node.js. And it’s easy to learn. That’s why I would like to use this.</p><p><strong><em>Cucumber.js</em></strong></p><p>Cucumber is a BDD test framework. You code test cases here. The most important advantage of it is the human readable syntax. Non-programmers can easily contribute to the code base.</p><p><strong><em>Docker</em></strong></p><p>Docker is now the famous tool that help setting up systems. With the help of DockerHub, lots of systems can be easily constructed by a few commands. This is the key part of the solution.</p><h3>Situations</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bbxHOkbfqkJelJ_MU5YnWw.jpeg" /></figure><p>There are three situations (or problems to solve). Different solutions make us of different setups. They can be compared in the views of difficulty and scalability.</p><p><strong><em>Quick Development</em></strong></p><p>It’s the case that we are most likely to face. We have lots of QAs that know nothing about server setup. They just know some basic programming skills.</p><p><strong>Our concerns:</strong></p><ol><li>installing libraries and servers is a difficult task</li><li>installing tools is kind of making your computer “dirty”</li><li>too many ways to install tools. there may be lots of differences among them.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WD1HDMoLk6iOvE7uJoiRxA.jpeg" /></figure><p><strong>my expect outcome:</strong></p><ol><li>QAs do not need to install too much tools, except docker and git.</li><li>QAs can use their own text editors to code</li><li>QAs don’t need to handle any server config</li><li>QAs can just destroy and recreate the system easily when facing weird issues</li></ol><p><strong>my solution:</strong></p><ol><li>QAs download code by git</li><li>QAs install docker</li><li>Developers config the whole docker-compose file. Actually, for Selenium Grid, you can easily copy the whole setting from internet.</li><li>QAs run ‘docker-compose up’ to start the whole selenium grid</li><li>the ‘docker-compose up’ will also start a node.js container to run javascript</li><li>with docker’s volume mounting, QAs can just edit code in the host computer.</li><li>with docker’s volume mounting, QAs don’t need to download application’s related packages. They all are installed inside the docker container.</li><li>QAs edit code in the host, and run test command inside the container.</li><li>DONE!</li></ol><p><strong>Problems left:</strong></p><ol><li>DockerHub doesn’t have the selenium-safari image. Safari cannot be tested.</li><li>As the browsers are inside the docker containers, you can’t see the browser running. ( Yet, when you literally start writing code, you won’t spend time on the browsers)</li></ol><p><strong><em>Deep-dive debugging</em></strong></p><p>It’s the case that we will face the least. There should be cases that we really need to see the browser running (though I don’t even meet this situation, logically we need to plan it also). It’s exactly the opposite of the previous solution. I won’t spend too much time on this.</p><p><strong>Our concerns:</strong></p><ol><li>sometime we need to find out how steps being run</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wq6vww8EOHbRIWjxZIbTJQ.jpeg" /></figure><p><strong>my solution:</strong></p><ol><li>install the whole stack, git, node, selenium, all browsers…</li><li>do coding and run commands in the host computer</li></ol><p><strong>Problem left:</strong></p><ol><li>a lots… just the opposite of the previous solution</li></ol><p><strong><em>Regression test</em></strong></p><p>Other than developing test cases, we always need to do regression tests before deploying.</p><p><strong>Our concerns:</strong></p><ol><li>Regression test should involve Safari.</li><li>the host should be powerful enough to run these huge amounts of test cases as fast as possible</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*m4AvP06wmrsef4MWe-o8Mg.jpeg" /></figure><p><strong>my solution:</strong></p><ol><li>as the setup will be a bit more complicated, we better setup a server to handle this. QAs can start the regression test by send HTTP request from their computer. Deployment tools, like Jenkins, can also run test on it.</li><li>using Docker compose to build a selenium grid like the quick-development solution.</li><li>also install one more selenium in the host. this selenium will handle all the Safari testing.</li></ol><p><strong><em>Whole Picture</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RAHDKz2K6KjAJ7k7m-3zKA.jpeg" /></figure><p>To conclude, we can setup two regression test servers. One for Production, one for Development and QA environment. Based on the developer’s skill level, they can use the ‘Quick Development’ or ‘Deep-dive Debugging’ aproaches.</p><h3>The remaining part !</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EdWxIHZ9VNM5-2ggOPd2vQ.jpeg" /></figure><p>The remaining part is the Cucumber layer. It’s a huge topic. I am still figuring it out. I have built a framework to some basic application management. Feel free to check this out:</p><p><a href="https://github.com/Tedc0819/cucumber-web">Tedc0819/cucumber-web</a></p><p>It mainly handle the config massaging and page-object concept. Go and take a look of the README.md</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d01482786e6c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[my Automation Test Framework based on mochajs]]></title>
            <link>https://medium.com/@tedchength/my-automation-test-framework-based-on-mochajs-d1a5bffb46b3?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/d1a5bffb46b3</guid>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[mocha-js]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[unit-testing]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Tue, 09 May 2017 05:21:14 GMT</pubDate>
            <atom:updated>2017-05-09T15:40:21.390Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*W4ntkSbwOZXEysOvvOUzGQ.jpeg" /></figure><p>In the last Article, I talked about how test cases benefit your development. Honestly, it was a boring article. In this article, I am going to talk about how I construct my test framework. Basically, it’s a framework based on <strong><em>mocha</em></strong>.</p><h4>Objective:</h4><p>Create a user-friendly test framework that:</p><ol><li>only manage flow of the test</li><li>will generate test cases by setting</li><li>mainly handle Unit Test (the framework is just a generator, you can use it to handle integration test also)</li></ol><p>Before talking about the design, there are a few things you need to understand. And I do believe they are the most basic principles of programming.</p><h4>Always remember HOW, WHEN, WHERE, WHAT.</h4><p>No matter which framework you use, for example, MVC, MVVM, React, you will realise that they are in everywhere. The most obvious example is MVC.</p><p>If you follow the ‘FAT MODEL, THIN CONTROLLER’, you will find that model is responsible of HOW to do. The controller is responsible of WHAT to do.</p><p>Let’s say you need to implement an API to apply a job. And users need to be authenticated before they can do so. You can immediately draft the methods:</p><ol><li>Session.authenticate — it handles HOW to authenticate the user. It will do a query to see if a valid session record exist in Redis Server</li><li>AppliedJob.applyJob — it handles HOW to apply a job. It will create a AppliedJob record or return error after some checkings.</li><li>AppliedJobController.create — it handles the api request by controlling ‘WHEN to do WHAT’. It will call Session.authenticate to verify user and then AppliedJob.applyJob to complete what the actual API need to do.</li></ol><h4>Give up Flexibility and Gain Efficiency</h4><p>Every time you refactor, you are actually reordering codes based on some assumptions or some goals to achieve. If you find that you code does not look ‘good’. Maybe it’s just you can’t defined your objectives well.</p><p>One of the example is React. Nowadays webpages become complicated and are with lots of interaction. People found that it was so difficult to trace the error in front-end development. They ASSUMED that the data flow was the main cause of the problem. Then set their GOALs to provide a unidirectional data flow. They were giving up flexibility of data flow to gain the efficiency of handling the architecture of the application.</p><h4>Define roles, interfaces and layers</h4><p>Every good framework defines roles, interfaces and layer very well. For example, most of the back-end frameworks consist of Router, Middleware, Controller, Model and Helper. If you are a back-end developer, you will understand it immediately when you hear their names. And you will say ‘when the client send a request, the router will route it to that controller and those models are going to do the stuff’. You won’t say ‘when the client send a request, it will be compared with a table to see where to go first, and then, that function will be call to generate blah.. blah.. blah…’. It is simply because people understand their roles.</p><p>Also, for React, it talks about Store, Reducer, Dispatch, Action and Prop. Store is defined to store the data. Reducer handle WHAT to do when actions are dispatched. Action is a well-defined struct. Their roles are clear. They are the core of React. Besides, ActionCreation is also created for centralising HOW the action is created. Somebody may even slightly redefine this component to also handle asynchronies dispatch. Yet it is another story. The key point is ‘Always remember what components are and which one is closer to the core part of the system’. By understanding the layers, you will know where to put the code handling business cases.</p><h3>Assumptions</h3><p>Most of the time, and obviously, a test case tests a certain point of a system, especially for unit test. The test logic flow like this ‘Under that FIXTURES, when I apply these ARGUMENTs, that POINT should give that RESULT’. Point to note is that a point does not mean to be a method. Also, arguments do not mean the arguments to the method, but to the test point.</p><p>When people try to test a point, they will think ‘ When I input a correct user id and a correct job id, the AppliedJob.applyJob should create a AppliedJob record and return that record.’ Unfortunately, when they literally implement the test case, they are actually thinking like this “When I input user id 113344 and job id 33, the AppliedJob.applyJob should create a AppliedJob record and return that record.’</p><h4>Define interface for argument values using labels</h4><p>To simplify, I will assume the test point now is just testing one method. Say, AppliedJob.applyJob(userId, jobId). For unit testing, we can easily plan that what cases will happen to userId and jobId.</p><p>For user Id:</p><ol><li>correct user id that exists in the database</li><li>incorrect user id that does not exist in the database</li><li>maybe null will also be the input.</li></ol><p>For job id, it should be similar.</p><ol><li>correct job id that exists in the database</li><li>incorrect job id that does not exist in the database</li><li>maybe null will also be the input.</li></ol><p>Actually you can foresee that we can construct a 2D array to indicate the cases. Let say they are called argumentType (argType). so let’s define:</p><pre>//it’s good to have the same order to the method arguments<br>args = [‘userId’, ‘jobId’]</pre><pre>// according to the order above<br>argTypes: [<br>  [&#39;correct&#39;, &#39;notExist&#39;, &#39;null&#39;], //indicates the cases of userId<br>  [&#39;correct&#39;, &#39;notExist&#39;, &#39;null&#39;]  //indicates the cases of jobId<br>]</pre><pre>//We can easily generate all the combinations<br>[&#39;correct&#39;, &#39;correct&#39;] //means correct userId and correct jobId<br>[&#39;correct&#39;, &#39;notExist&#39;] //means correct userId and wrong jobId<br>//blah blah blah ...</pre><p>It’s going to be the key skeleton of the whole framework.</p><h4>Mocha</h4><p>Another key point of the framework is mocha. Our framework only helps generate mocha test cases. The mocha is still the core part. I am not going to talk too much on what mocha do, but more on how our framework targets to help the mocha test cases.</p><p><a href="https://mochajs.org/#getting-started">Mocha - the fun, simple, flexible JavaScript test framework</a></p><p>Before this framework, I tried to write my test cases like this to cover all the cases:</p><pre>describe(&#39;AppliedJob.applyJob - (userId, jobId)&#39;, <strong>function</strong>() {</pre><pre>  describe(&#39;success - [correct, correct]&#39;, <strong>function</strong>() {</pre><pre>    it(&#39;should return an AppliedJob record&#39;, <strong>function</strong>() {<br>      // assert AppliedJob record<br>    });</pre><pre>  });</pre><pre>  describe(&#39;failure - [correct, notExist]&#39;, <strong>function</strong>() {</pre><pre>    it(&#39;should return Error blah blah blah...&#39;, <strong>function</strong>() {<br>      // assert error<br>    });</pre><pre>  });</pre><pre>  describe(&#39;failure - [correct, null]&#39;, <strong>function</strong>() {</pre><pre>    it(&#39;should return Error blah blah blah...&#39;, <strong>function</strong>() {<br>      // assert error<br>    });</pre><pre>  });</pre><pre>  // an all the other combinations</pre><pre>});</pre><p>Now, with the combinations array, we can now do it like this:</p><pre>describe(&#39;AppliedJob.applyJob - (userId, jobId)&#39;, <strong>function</strong>() {</pre><pre>  combinations.forEach(function(combination) {</pre><pre>    var isSuccess = shouldSuccess(combination);</pre><pre>    it(&quot;${isSuccess} - ${combination}&quot;, function() {</pre><pre>      //assertion based on combination</pre><pre>    })</pre><pre>  })</pre><pre>})</pre><p>Of course, this is just code of the prove of concept. There is much more to handle, like phasing of mocha(e.g. afterEach, beforeEach). I am still working the tutorial and README of the framework. To keep track, please visit:</p><p><a href="https://www.npmjs.com/package/mocha-combo">https://www.npmjs.com/package/mocha-combo</a></p><p>If you are really interested on this idea and can&#39;t wait for the tutorial, leave me a message here, I will try my best to help you.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d1a5bffb46b3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Write Test Case, Engineers.]]></title>
            <link>https://medium.com/@tedchength/write-test-case-engineers-7f21b469e978?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/7f21b469e978</guid>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[backend]]></category>
            <category><![CDATA[test-driven-development]]></category>
            <category><![CDATA[test-automation]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Tue, 28 Mar 2017 07:29:31 GMT</pubDate>
            <atom:updated>2017-03-28T07:29:31.442Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/400/1*uB8rm7Ld_6zAJCOLkRZK0Q.gif" /></figure><p>Writing test cases has already become the most important part of my work flow. It was really a hard journary. I started this kind of test-driven develop for 3 projects. They were mainly backend applications. After spending lots of time on refactoring, refining my concepts about test case and presenting my ideas to my co-workers, I would like to share my thoughts and experience in this article. Maybe I will also write another article to talk about the framework that I wrote for testing and its design principle.</p><h3>The workflow diagram</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/279/1*_ECsB_e_wgkLOBGDHvB8EA.png" /></figure><p>Everyone knows that things will be easy if there is a QA engineer in you team. Unfortunately, most of the time, there is only a QA team doing manual testing. Therefore, the above work flow is important.</p><p>Let’s say you are developing a system with test cases and assume your test cases can cover 50% cases of the features. After manual testing, your QA member discover 20% more cases. And you code these into test cases so that your test cases are now covering 70% of cases. You can also foresee that, with this kind of feedback loop, the test coverage will be in a trend of increasing.</p><h3>Writing Test Cases don’t mean that QA do less work</h3><p>I also struggled about how we should separate roles on these testing tasks. I would like to generalise it like this.</p><blockquote>Engineers do cases that easy to foresee. QAs do the edge cases and the double-check.</blockquote><p>It’s so obvious that the value of QA is to think and draft the test flow. They spend lots of the time to ask ‘what if ‘. “What if we don’t follow the flow and click another button?”, “What if we input blah… blah… blah…”. These ‘what if ‘ will finally become edge cases.</p><p>On the other hand, the value of engineers is to develop the application. Unlike the QAs, they have no time to ask too much ‘what if’. That why they just need to test cases that can be easily thought of. Practically, the cases tested by engineers, during the first phase of testing, are mainly based on the features’ acceptance criteria and also the combination of inputs.</p><h3>Test Cases give you sustainable development</h3><h4>Existing features are safe when you and your teammates develop new ones.</h4><p>Your teammates just need to run all the test cases before they push the code. If all the cases pass, everything will be safe. The test cases also help finding out issues if the cases fail.</p><h4>Refactoring becomes easy.</h4><p>With all test cases pass, it somehow implies that the whole project is in a stable state. As long as your test cases all pass, you can do any kind of refactoring. No matter how big your refactoring is, the test cases ensure that you application still fulfils all the existing acceptance criteria of features.</p><p>Sometimes you may also need to refactor your test cases. It’s also easy.</p><ol><li>ensure all test cases pass.</li><li>Don’t make any further changes on the application code.</li><li>update your test cases.</li></ol><p>Easy to understand, right? To be honest, this the most enjoyable advantage.</p><h4>Test cases are lists of acceptance criteria and spec for features</h4><p>Every test case exists for a reason. Everytime you run all the test cases, the result is kind of describing the current state of the application. “Under what scenario, with what kind of input, it should return what” or “What API should be able to paginate”</p><h4>Applications can learn from failure</h4><p>With the work flow stated above, your QA teammates help you to improve the coverage of your test cases. Your application can learn from new bugs.</p><p>Imagine if there is no test cases, everytime a new bug comes, you will just fix it. There will be no place to “jot down” the unexpected behaviour and also ensure it won’t happen again along the development.</p><h3>Writing Test Cases can be efficient</h3><p>I guess time is the biggest hurdle of writing test cases. Especially, you start this practice from your office work. You may need to convince you boss with the benefits it brings. And most of the time you teammates only care about how tight the schedule is. Yet.</p><blockquote>You can never fly if you don’t keep trying.</blockquote><p>To be honest, I worked over time for lots of days and kept refactoring the tests under different assumption. I definitly a huge investment. Yet I do believe that the final outcome will save me lots of time on moving cursor, clicking browsers or Postman and checking the result with my eyes. I can simply run the test to verify my work.</p><p>When I started working with test cases, I spent almost <strong><em>90%</em></strong> on test cases and <strong><em>10%</em></strong> on implementation. At that time, the test cases were just even the simplest ones. No matter how I refactor my code, the things just didn’t seem right. The efficiency didn’t seem improved much. The code wasn’t also look nice and easy to maintain.</p><p>I started another project. This time I tried not to refactor the code. Made every test case look seperated. Hoped that every teammate can contribute without understanding much about the structure. Of course, this was a stupid thought. Things still went worse as the project grew bigger.</p><p>Fortunately, I figured out what went wrong. What we need is a framework. I spent lots of time on thinking how it should be. And of course, after some experiment, it worked just right. Even my boss enjoy using my test framework. And they can easily code the test cases by following a few examples. Now we spent 60% time on test cases and 40% on implementation. Of course, the develop time is also shortened. I will write another article about the main idea of the test framework. Quite a lot to explain every decision I made. Maybe you can write your own in different languages.</p><h3>Write Test Case, Engineers.</h3><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7f21b469e978" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Installing Docker using Ansible Script]]></title>
            <link>https://medium.com/@tedchength/installing-docker-using-ansible-script-c182787f2fa1?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/c182787f2fa1</guid>
            <category><![CDATA[deployment]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[ansible]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Thu, 12 Jan 2017 07:54:47 GMT</pubDate>
            <atom:updated>2017-01-12T07:55:07.883Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wL28qQmH77O0K-iP8MKO3g.png" /></figure><p><a href="https://medium.com/@tedchength/basic-deployment-with-ansible-docker-jenkins-and-git-4f3b42fb2f5e">Basic (Continuous Integration?) Deployment with Ansible, Docker, Jenkins and Git</a></p><p>If you have read my last article, you will understand why I script my Docker Installation using Ansible.</p><p>For Ansible installation guide:</p><p><a href="http://docs.ansible.com/ansible/intro_installation.html">Installation - Ansible Documentation</a></p><p>In this tutorial, we are using:</p><ol><li>Ansible 2.0.2</li><li>Docker 1.12.0</li><li>Ubuntu 14.04</li></ol><h3>Objectives:</h3><ol><li>Make the installation process repeatable</li><li>Make it easier for teammates to learn about the process</li></ol><h4>Folder Structure :</h4><pre>ansible:           //root of folder</pre><pre>  group_vars:      //storing variable according to different env<br>    develop.yml<br>    staging.yml<br>    production.yml</pre><pre>  inventories:     //grouping host according to env<br>    develop<br>    staging<br>    production</pre><pre>  roles:           //defining roles and their related tasks   </pre><pre>    common:        //handling common setting like: user setup<br>      tasks:<br>        main.yml<br>      templates:</pre><pre>    dockers:       //handling dockers installation.<br>      tasks:<br>        main.yml<br>      templates:<br>  <br>  deploy-docker.yml //the main playbook for the task &#39;deploy-docker&#39;<br>  deploy-docker.sh  //shell script wrapping the corresponding .yml</pre><blockquote>the ‘docker-docker.sh’ is only my practice based on <strong>WHEN-WHAT-HOW-WHERE-to-do</strong> principle mentioned in last article. This practice make my <strong>Jenkins</strong> just need to know which script to run but know nothing about the parameters. Scroll to the top and read it for details.</blockquote><p>Let assume we just want to deploy to develop environment.</p><h4>inventories/develop:</h4><pre>[docker]<br>10.10.10.1<br>10.10.10.5<br>10.10.10.8</pre><pre>[develop:children]<br>docker</pre><pre>[develop:vars]<br>ansible_ssh_private_key_file=__YOUR_SSH_KEY_FILE_PATH__</pre><p>Assuming we need three docker instances, we define the three IPs in the group named <strong><em>docker</em></strong>. Also, we define a group <strong><em>develop</em></strong> and state that <strong><em>docker</em></strong> is the sub group of develop. The [develop:children] is important. It tells Ansible that the hosts under <strong><em>docker </em></strong>belongs to <strong><em>develop </em></strong>and will use the <strong><em>group_vars</em></strong> stated in <strong><em>develop.yml</em></strong>.</p><h4><strong><em>group_vars/develop.yml:</em></strong></h4><pre>---<br>//user account used to ssh the hosts<br>remote_user: ubuntu            </pre><pre>//non-sudo user to be created for running process<br>action_user: tedcheng</pre><pre>//password for the non-sudo user, actually we can have better way to not storing the password in code, but this is for simplifying the tutorial.</pre><pre>action_user_pw: __complicatedPW__<br></pre><h4><strong>roles/common/tasks/main.yml :</strong></h4><pre>---</pre><pre>- name: setup Action User<br>  user: generate_ssh_key=yes name={{action_user}} password={{action_user_pw | password_hash(&#39;sha512&#39;) }} shell=/bin/bash</pre><p>Basically, the tasks in <em>common</em> should be simply creating a action user. The action user is for starting some process that you don’t what it to have too much access right of the system files.</p><blockquote>Please beware that the password needed to be hashed. For more information, just google “ansible password_hash”</blockquote><pre>// if you need any proxy setup<br>---</pre><pre>- name: setup apt-get proxy<br>  file: path=&quot;{{apt_proxy_file}}&quot; state=touch</pre><pre>- lineinfile: dest=&quot;{{apt_proxy_file}}&quot; line=&quot;{{item}}&quot;<br>  with_items:<br>  - &#39;Acquire::http::Proxy &quot;__YOUR_PROXY_URL__&quot;;&#39;<br>  - &#39;Acquire::https::Proxy &quot;__YOUR_PROXY_URL__&quot;;&#39;</pre><pre>- name: touch ~/.wgetrc and setup wget proxy<br>  file: path=&quot;~/.wgetrc&quot; state=touch</pre><pre>- lineinfile: dest=&quot;~/.wgetrc&quot; line=&quot;{{item}}&quot;<br>  with_items:<br>  - &quot;use_proxy=yes&quot;<br>  - &quot;http_proxy=__YOUR_PROXY_URL__&quot;<br>  - &quot;https_proxy=__YOUR_PROXY_URL__&quot;</pre><pre>- name: setup Action User<br>  user: generate_ssh_key=yes name={{action_user}} password={{action_user_pw | password_hash(&#39;sha512&#39;) }} shell=/bin/bash</pre><pre>- name: touch ~/.wgetrc and setup wget proxy<br>  file: path=&quot;~/.wgetrc&quot; state=touch<br>  become_user: &quot;{{remote_user}}&quot;</pre><pre>- lineinfile: dest=&quot;~/.wgetrc&quot; line=&quot;{{item}}&quot;<br>  with_items:<br>  - &quot;use_proxy=yes&quot;<br>  - &quot;http_proxy=__YOUR_PROXY_URL__&quot;<br>  - &quot;https_proxy=__YOUR_PROXY_URL__&quot;<br>  become_user: &quot;{{remote_user}}&quot;</pre><p>In case your hosts’ network need a proxy server for connections outside the network, you may need to setup the proxy. This is my script for one of my project in production.</p><p>It setup proxy for:</p><ol><li>apt-get</li><li>wget of the remote user (the sudo user)</li><li>wget of the action user (the non-sudo user for running application processes)</li></ol><h4>roles/docker/tasks/main.yml :</h4><p>Actually I just turn the steps from the official website into Ansible script.</p><p><a href="https://docs.docker.com/engine/installation/linux/ubuntulinux/">https://docs.docker.com/engine/installation/linux/ubuntulinux/</a></p><pre>---<br>- name: install docker<br>  command: sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D</pre><pre>- name: add deb repo<br>  command: sudo touch /etc/apt/sources.list.d/docker.list</pre><pre>- lineinfile: dest=&quot;/etc/apt/sources.list.d/docker.list&quot; line=&quot;{{item}}&quot;<br>  with_items:<br>    - &quot;deb https://apt.dockerproject.org/repo ubuntu-trusty main&quot;</pre><pre>- name: apt update<br>  apt: update_cache=yes</pre><pre>- apt: name=docker-engine state=present</pre><pre>- name: add user mod<br>  command: sudo usermod -aG docker {{remote_user}}</pre><pre>- name: add user mod<br>  command: sudo usermod -aG docker {{action_user}}</pre><p>The last two steps are required for let the two users access the docker engine.</p><pre>// if you need any proxy setup</pre><pre>---<br>- name: install docker<br>  command: sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --keyserver-options http-proxy=__YOUR_PROXY_URL__ --recv-keys 58118E89F3A912897C070ADBF76221572C52609D</pre><pre>- name: add deb repo<br>  command: sudo touch /etc/apt/sources.list.d/docker.list</pre><pre>- lineinfile: dest=&quot;/etc/apt/sources.list.d/docker.list&quot; line=&quot;{{item}}&quot;<br>  with_items:<br>    - &quot;deb https://apt.dockerproject.org/repo ubuntu-trusty main&quot;</pre><pre>- name: apt update<br>  apt: update_cache=yes<br>  environment:<br>    http_proxy: __YOUR_PROXY_URL__<br>    https_proxy: __YOUR_PROXY_URL__</pre><pre>- apt: name=docker-engine state=present<br>  environment:<br>    http_proxy: __YOUR_PROXY_URL__<br>    https_proxy: __YOUR_PROXY_URL__</pre><pre>- lineinfile: dest=&quot;/etc/default/docker&quot; line=&quot;{{item}}&quot;<br>  with_items:<br>  - &quot;export http_proxy=\&quot;__YOUR_PROXY_URL__\&quot;&quot;<br>  - &quot;export https_proxy=\&quot;__YOUR_PROXY_URL__\&quot;&quot;</pre><pre>- name: add user mod<br>  command: sudo usermod -aG docker {{remote_user}}</pre><pre>- name: add user mod<br>  command: sudo usermod -aG docker {{action_user}}</pre><p>This is also the setup for docker in case you need a proxy. One point to note is the “lineinfile: dest=”/etc/default/docker”. It enables the docker to download some public docker images through a proxy.</p><h4>ansible/deploy-docker.yml:</h4><pre>---</pre><pre>- hosts: docker              // define &#39;docker&#39; as our action target<br>  remote_user: &quot;{{remote_user}}&quot; //define user account for ssh login<br>  become: yes<br>  become_method: sudo<br>  serial: 1          //define the action will be done one by one<br>  roles:<br>  - role: common     //define &#39;common&#39; role should be apply to hosts<br>  - role: docker     //define &#39;docker&#39; role should be apply to hosts</pre><h4>ansible/deploy-docker.sh</h4><pre>ANSIBLE_HOST_KEY_CHECKING=false ansible-playbook main.yml -i inventories/develop</pre><p>ANSIBLE_HOST_CHECKING is mainly for skipping the (yes/no) question of known_hosts checking for ssh</p><p>Finally, you can run</p><pre>sh deploy-docker.sh</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c182787f2fa1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Basic (Continuous Integration?) Deployment with Ansible, Docker, Jenkins and Git]]></title>
            <link>https://medium.com/@tedchength/basic-deployment-with-ansible-docker-jenkins-and-git-4f3b42fb2f5e?source=rss-6f4cf106090e------2</link>
            <guid isPermaLink="false">https://medium.com/p/4f3b42fb2f5e</guid>
            <category><![CDATA[ansible]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[jenkins]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Ted Cheng]]></dc:creator>
            <pubDate>Tue, 10 Jan 2017 09:20:03 GMT</pubDate>
            <atom:updated>2017-01-10T10:42:29.141Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/235/1*VYUFezyGQ56W-EovPRtY1Q.jpeg" /></figure><p>Recently, I tried to build a Continuous Integration (CI) deploy flow for my team. Then I start this study alone, and come up with these thoughts.</p><p>In the end, as we still didn’t finish all the test cases and we decided to skip it at this moment. It will be shameful if we call this deployment a CI flow. ANYWAY.</p><p>This article mainly provides the concept of the architecture. I won’t talk too much on how the precise steps being implemented.</p><h3><strong>Objectives:</strong></h3><ol><li>Jenkins manages <strong>WHEN</strong> and <strong>WHAT</strong> to do.</li><li>Ansible manages <strong>HOW</strong> and <strong>WHERE</strong> to do.</li><li>Docker <strong>ISOLATE</strong> your application with the host, if the host can run docker, you can deploy your application.</li></ol><h3><strong>What scenarios will be achieved:</strong></h3><ol><li>Your teammates can help doing the deployment simply by clicking buttons using Jenkins.</li><li>Everything is scripted. Tasks will be repeatable and debug-able.</li><li>Deployment script will be put in the application directory and managed by git at the same time. This is a benefit if you are in a same time and doing application and system management at the same time.</li></ol><p>4. of course there are much more …</p><h3>Four main tools:</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/874/1*ZPYJimL0vzyIQXsG9eC5YQ.png" /></figure><p><a href="http://docs.ansible.com/ansible/intro.html#">http://docs.ansible.com/ansible/intro.html#</a></p><p>To me, Ansible is the best tools for managing my deployment code, because:</p><blockquote><strong>You write commands to set the hosts’ states instead of running an action.</strong></blockquote><p>No more wasted time on writing if-else cases to check a file’s existence and delete it. The only thing you need to do is just state it as absent.</p><pre><em>file: path=&quot;/etc/some_directory&quot; state=absent</em></pre><p>details: <a href="http://docs.ansible.com/ansible/file_module.html">http://docs.ansible.com/ansible/file_module.html</a></p><blockquote><strong>You manage your hosts by grouping and defining roles</strong></blockquote><p>You can describe 10.100.222.1–10 are api-hosts. describe api-hosts need ~/.ssh/deploy.pem to ssh for deployment.</p><p>You can describe 10.100.222.11–13 are mongodb-replica-set. and 10.100.222.13 will be mongodb-backup-source.</p><p>These kind of grouping and making roles help you to seperate <strong>HOW</strong> and <strong>WHERE</strong> to do the task very well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*s-YgL1NW7evYyCBWI-7w8Q.png" /></figure><p>Docker is a tool that I love and hate it at the same time. The good things are:</p><blockquote><strong>Isolate the environment setup from the actual host machine by providing you a VM-like Docker container.</strong></blockquote><p>No matter what host machines your infra team gives you, you can deploy to these machines provided that they run Docker. Also, you can control what OS your application running in whatever the host machines are actually running</p><blockquote><strong>Deploy multiple applications on the same hosts thanks to the Docker containers.</strong></blockquote><p>It is always painful when you deploy multiple application on the same host machine. Image your have two sails projects, one is running nodejs:5.9.2 and the other is running nodejs: 6.0.0. It is will be a trouble the you need to config the node version for the two projects. Fortunately, with Docker container, you just need to specify the node version and installation in the separate Dockerfile. And the nodejs will be installed in separate containers. The host machine will just need to run the two containers and things are done.</p><p>Besides, for some macro service architecture, it will be much easier to setup if you need to run multiple services on the same host.</p><blockquote><strong>Large community provides different kinds of basic Dockerfile</strong></blockquote><p>There are lots of basic Dockerfile that you can start with writing your own, like nodejs and rails. Even you can just start a Jenkins server by using</p><pre>docker run jenkins</pre><p>The Docker engine will pull the required DockerImage and run the Jenkins.</p><h4><strong>However</strong>, there are down sides of using Docker. Yet, to be honest, when you are an experienced Docker user, you will solve these problems easily.</h4><blockquote><strong>A large DockerImage will result in a slow export, import and shipping DockerImage among hosts.</strong></blockquote><p>The first thing you <strong>SHOULD</strong> <strong>KNOW </strong>about writing Dockerfile is how the DockerImage is actaully built from the Dockerfile you wrote.</p><p>One of the terrible thing is like this, in simple words, you write you Dockerfile command by command and each command will create a layer to record the actual system change by the command. For example, your first command delete 50MB files from the system. And then your second command add 30MB file to the system. The layers will treat it as a 80MB file change in total.</p><p>Of course, there are best practices to avoid this senarios.</p><blockquote><strong>Dockerfile should be well written in sequences to make well use of caches</strong></blockquote><p>Everytime you build a DockerImage, the Docker engine will cache the layers so that the building process will be faster next time.</p><p>One the example is that, for a nodejs project, it is always needed to install packages and also copy the application code into the DockerImage. These two actions both changes the result of the DockerImage and affect the caches. However, comparing the frequency of these two actions, you will find that running the installation of packages will affect the caches less.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/676/1*fiOou8ZYaZe-Ka8NUyovMw.png" /></figure><p>A well-known tools manage tasks for servers. Actually, not much I can talk about Jenkins. It just easy to use and powerful.</p><blockquote><strong>Powerful plugins for hooking up the Jenkins and Github</strong></blockquote><p>For git users, it is a common task for you to hook up Jenkins and Github for deployment. Once you code changes in Github, your Jenkins get notified. Also, this kind of hooking is an essential parts of a Continuous Integration System.</p><p><strong>GitHub plugin</strong></p><p><a href="https://wiki.jenkins-ci.org/display/JENKINS/GitHub+Plugin">Jenkins : GitHub Plugin</a></p><p>This one is what I am using.</p><blockquote><strong>Easy to setup slaves to scale your tasks in Jenkins</strong></blockquote><p>There are lots of cases that you need to create slaves for your Jenkins. for example,</p><ol><li>There are lots of tasks to do. And you need more workers</li><li>There are some tasks require certain environment.</li></ol><p>The first one is easy to understand. For the second one, for example, we need to deploy a DockerImage to some hosts when Jenkins noticed code is changed in Github. Where to pull the code? Where to build the DockerImage? In this case, you will need a Jenkins slave with Docker installed (it’s actually just a ubuntu installed Docker) to handle all the Docker related stuff.</p><p><em>( You can say that why not just install Docker in the Jenkins host. The main design concepts to me are:</em></p><p><em>ALWAYS MINIMISE THE NUMBER OF KINDS OF BUILDING UNIT IN YOUR SYSTEM.</em></p><p><em>DON’T MAKE YOUR BUILDING UNIT COMPLICATED.)</em></p><p>The plugin that help me a lot when setting up slaves is this:</p><p><strong>SSH Slaves Plugin</strong></p><p><a href="https://wiki.jenkins-ci.org/display/JENKINS/SSH+Slaves+plugin">SSH Slaves plugin - Jenkins - Jenkins Wiki</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/325/1*zzvdRmHGGXONZpuQ2FeqsQ.png" /><figcaption>Git</figcaption></figure><p>If you are already using Ansible, Docker and Jenkins, I am sure you know what is Git and what it helps.</p><h3><strong>Two Dimensions Deployment:</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/838/1*bN5r8OLRIsLLaE-mQnKGAQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/836/1*ncqo3ZKBne16sNfhPeecGw.png" /></figure><p>I try to separate deployment flow, in concept, into two dimensions, vertical and horizontal.</p><blockquote>The vertical one focuses on how code change become a result of an application update in a server.</blockquote><p>This dimension is solved mainly by:</p><ol><li>the hook between Jenkins and Git</li><li>the Docker and Ansible slaves setup</li><li>Build, export, ship and import the DockerImage from Jenkins’ slave to the host by Ansible script.</li></ol><blockquote>The horizontal one focuses on how a group of hosts are updated.</blockquote><p>This dimension is solved mainly by:</p><ol><li>Jenkins tasks start an Ansible script.</li><li>Ansible ship and import the images in parallel</li><li>Ansible restart the Docker containers in servers one by one to minimise down time.</li></ol><h3><strong>So Now…</strong></h3><p>Let’s try to talk about some real-life cases:</p><p>Assuming we are going to have API servers and Web servers and we are going to have a auto-deploying system (or Continuous Integration) in staging and dev and a click-to-deploy flow in production.</p><h3>Our building units will be:</h3><ol><li><strong>Docker Instances</strong></li></ol><p>Yeah, I can’t list more. Let see how we build with only Docker Instances.</p><h4><strong>Jenkins</strong></h4><ol><li>Just run this in the Docker instance</li></ol><pre>docker run jenkins</pre><p>2. Install plugins, hook GitHub and build slaves (maybe talk about it in another article)</p><h4><strong>Jenkins Slaves</strong></h4><ol><li>With Docker installed, you just need to install Ansible in this instance.</li></ol><pre>$ sudo apt-get install software-properties-common<br>$ sudo apt-add-repository ppa:ansible/ansible<br>$ sudo apt-get update<br>$ sudo apt-get install ansible</pre><h4>API Server and Web Server</h4><ol><li>With Docker installed, nothing more you should do. Just leave the jobs to Ansible script to start a Docker container.</li></ol><h4>MongoDB</h4><ol><li>Just run this in the Docker instance</li></ol><pre>docker run mongo</pre><p>(of course, you need to write a config file for replica-set, and mount volumes to store the data out of the Docker Container, but these can be scripted)</p><h4>Redis</h4><ol><li>Just run this in the Docker instance</li></ol><pre>docker run redis</pre><p>(of course, you need to write a config file for replica-set, and mount volumes to store the data out of the Docker Container, but these can be scripted)</p><blockquote><strong><em>The Evil Thing</em></strong></blockquote><blockquote>If you are clear about this building concept, you will release that, except Jenkins requiring some unavoidable manual work:</blockquote><blockquote>1. every instance only need some standard command to do the extra setup and start its service.</blockquote><blockquote>2. With standard commands, things can be scripted.</blockquote><blockquote>3. With scripts, this architecture can be reproduced easily.</blockquote><blockquote>Also, as every basic building unit is a Docker instance, you can write a script to do it!</blockquote><blockquote>So everytime you need to scale out, you receive a new host / create a new host on cloud. Then you run script to install docker on it, add the IP to the correct inventories. DEPLOY!</blockquote><h3>In Jenkins</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*VkP2oXN8edehzdcznBKfiw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/498/1*dOPhgR2wOasqRipP7JDKaQ.png" /></figure><blockquote><strong>Jenkins only control WHEN and WHAT to do</strong></blockquote><p>We can create different cron job and task in Jenkins with only simple command to start the Ansible job.</p><p>(As there are always some variables when running the Ansible commands, I usually wrap it with a shell script stored in the code base)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/719/1*kSiN34ven5LmvRJ7h08GPQ.png" /><figcaption>ansible/run.sh</figcaption></figure><h3>In the Code Base:</h3><blockquote><strong>Ansible control HOW and WHERE to do</strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/326/1*X8mrZu33HvRnllUpENHvsg.png" /></figure><p>For Ansible script, I mainly follow the official best practice.</p><p><a href="http://docs.ansible.com/ansible/playbooks_best_practices.html">LINK</a></p><ol><li>You can see we separate variables and hosts IPs and groups according to different environments.</li><li>For roles, I usually name them with the tasks’ names. It seems more flexible. <em>(any suggestions for this?)</em></li><li>The ***.yml at the bottom are the main playbooks to be run for different tasks.</li><li>The ***.sh is the wrapper for calling the Ansible command in order to simplify the script in Jenkins.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/336/1*kDNwTab6kSDaZhEi896hgQ.png" /></figure><p>For Docker, you can see we have Dockerfile, Dockerfile_ecg. This how we try to handle different environments if needed.</p><p>We use Ansible to replace the Dockerfile with a suitable one before we build the DockerImage.</p><p><em>(If you still remember, what the Dockerfile build should be independent of the host machine. But for our cases, since one of the environment needs proxy to do connection and one of the cloud is going to be deprecated, that’s why)</em></p><h3><strong>Summary</strong></h3><p>I know this article may be hard to understand since I just talk about concept but not the whole implementation like other tutorials outside. Yet, to me, having a deep thought on how architecture should be made is the most interesting thing in engineering. Concepts also give you a structure to consolidate your knowledge and build your own stuff. Also, it will be a lot to talk about if we write these in step by step.</p><p>Feel free to give suggestions or ask questions.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4f3b42fb2f5e" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>