<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Martin Maier</title>
    <description>The latest articles on DEV Community by Martin Maier (@_maimart_).</description>
    <link>https://dev.to/_maimart_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F85025%2Fdfa4b990-60a1-4ccf-bace-7eb2b026dd43.jpg</url>
      <title>DEV Community: Martin Maier</title>
      <link>https://dev.to/_maimart_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_maimart_"/>
    <language>en</language>
    <item>
      <title>Responsive Design with Protractor and Browserstack</title>
      <dc:creator>Martin Maier</dc:creator>
      <pubDate>Sat, 26 Jan 2019 08:07:22 +0000</pubDate>
      <link>https://dev.to/_maimart_/responsive-design-with-protractor-and-browserstack-1caa</link>
      <guid>https://dev.to/_maimart_/responsive-design-with-protractor-and-browserstack-1caa</guid>
      <description>

&lt;p&gt;This article describes how we can use Protractor and Browserstack to create a good environment and development process to quickly get feedback about the responsive design of a web application during development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge
&lt;/h2&gt;

&lt;p&gt;We want to develop a responsive web application. Therefore it should be able to be used in a user-friendly manner at various resolutions and in different environments.&lt;br&gt;&lt;br&gt;
So we want to make sure that the application is properly displayed and operable in different browsers under Windows, OSX, IOS and Android.&lt;br&gt;&lt;br&gt;
When viewing the app on the desktop, we distinguish between a maximized window and a reduced window, which may require a different layout. On mobile devices, we differentiate between portrait and landscape mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach
&lt;/h2&gt;

&lt;p&gt;During development, we want to get feedback on the presentation of our web application in the different environments as quickly and efficiently as possible.&lt;br&gt;&lt;br&gt;
To do this we want to create an environment in which we can verify the current state of our locally hosted app at any time from the different browsers of different operating systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Own devices
&lt;/h4&gt;

&lt;p&gt;We could now design our environment to be e.g. on a Mac and address the app hosted there from a virtualized or real Windows system. In addition, however, we still need mobile Android and IOS devices in our network to test our app from these as well.&lt;br&gt;&lt;br&gt;
However, this solution would require that we have to invest a lot of money in hardware. And this hardware would always have to carry with us and we may have to take big hurdles in order to operate all devices in our corporate network.&lt;br&gt;&lt;br&gt;
If we work with multiple developers in parallel or remotely, this solution is also unattractive.&lt;/p&gt;

&lt;h4&gt;
  
  
  Browserstack
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.browserstack.com/"&gt;Browserstack&lt;/a&gt; provides a virtual way of accessing web pages from various browsers, operating systems and devices. This can be used to manually or automatically test web applications.&lt;br&gt;&lt;br&gt;
Key feature for us is that we can also access our local environment from the Browserstack instances.&lt;br&gt;&lt;br&gt;
The license costs are not cheap depending on the needs, but they are still significantly lower than the hardware acquisition and configuration costs. It also allows us to develop efficiently and flexibly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Development process
&lt;/h3&gt;

&lt;p&gt;First we develop and optimize our web application with one of our favorite local browsers. We take into account the representation in maximum resolution and in small resolution. The latter may require adjustments via CSS MediaQueries etc.&lt;/p&gt;

&lt;p&gt;Next we want to get feedback on how the application looks in a variety of environments. For example, if we’re initially developing against Windows Chrome, we’d like to know how the app is displayed maximized and small in OSX Safari, IE11, Android Chrome and iPhone Safari.&lt;br&gt;&lt;br&gt;
For this we put the app via a protractor test in the state, whose representation we want to verify. Subsequently, a screenshot is created within the test and stored locally.&lt;br&gt;&lt;br&gt;
These semi-automated tests are run on Browserstack by using a respective protractor configuration. The result is a screenshot per desktop browser of the maximized window and a smaller window of defined size. For mobile devices, screenshots are taken in portrait and landscape mode.&lt;br&gt;&lt;br&gt;
This gives us automated and immediate feedback on these very different environments.&lt;br&gt;&lt;br&gt;
Problems with the presentation can then be analyzed and fixed manually via Browserstack Live. Within the Browserstack instances we are supported by the individual dev tools of the browser, as usual.&lt;/p&gt;

&lt;p&gt;When the app is properly displayed in all screenshots, we finally have to verify the remaining environments by hand.&lt;br&gt;&lt;br&gt;
We could also always make screenshots of all variants, but this would extend the capturing and viewing of screenshots accordingly.&lt;br&gt;&lt;br&gt;
If the app is neatly displayed in the most diverse environments, my experience is that there are rarely any problems with the rest of the environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note on the duration of the screenshots:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Depending on the license, the different environments can be tested in parallel in different instances and the tests can also be executed in parallel within the individual environments. Accordingly, the duration of the screenshot depends very much on how many parallel test executions the license contains. In my experience, taking screenshots takes about a minute per browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Browserstack
&lt;/h3&gt;

&lt;p&gt;First we create an account on the &lt;a href="https://www.browserstack.com/"&gt;Browserstack&lt;/a&gt; page with a license that allows you to run automated tests.&lt;br&gt;&lt;br&gt;
Logged in, we find in our username and the associated key in our settings.&lt;br&gt;&lt;br&gt;
We do not want to write this information as plain text in our configuration, so we put them in the environment variables of the OS as &lt;strong&gt;BS_USERNAME&lt;/strong&gt; and &lt;strong&gt;BS_KEY&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
For Browserstack to access our local instance we need the NPM package „browserstack-local“. We install this as described on the &lt;a href="https://github.com/browserstack/browserstack-local-nodejs"&gt;GitHub&lt;/a&gt; page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshoter
&lt;/h3&gt;

&lt;p&gt;For capturing and saving screenshots i wrote a small helper class. You can copy it from my &lt;a href="https://gist.github.com/maimArt/9dfaf1844dba43514b6ef1757e6549c4"&gt;GitHub Gist&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protractor configuration
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Dependencies
&lt;/h4&gt;

&lt;p&gt;In out protractor configuration we need &lt;strong&gt;browserstack-local&lt;/strong&gt; and the &lt;strong&gt;Screenshoter.&lt;/strong&gt;&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Screenshoter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./screenshoter'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browserstack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'browserstack-local'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Capabilities
&lt;/h4&gt;

&lt;p&gt;What I have referred as the environment is called capability in the context of protractor and Browserstack. Each capability defines an environment and can be created with the support of the &lt;a href="https://www.browserstack.com/automate/capabilities"&gt;Browserstack&lt;/a&gt; website.&lt;br&gt;&lt;br&gt;
Some properties of the capabilities are the same across. We define them globally and then inject them into the individual capabilities.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commonCapabilityProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;'browserstack.user'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BS_USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'browserstack.key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BS_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'browserstack.selenium_version'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'3.13.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'browserstack.local'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'browserstack.video'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'browserstack.debug'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'false'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'browserstack.timezone'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;BS_USERNAME&lt;/strong&gt; and &lt;strong&gt;BS_KEY&lt;/strong&gt; are our browser stacks username and key, which we have previously defined as the environment variable of the operating system. We pass them from the process at runtime.  &lt;/p&gt;

&lt;p&gt;Now we define our capabilities. This can look like this, for example:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;multiCapabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'OSX_Safari_1920'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;commonCapabilityProperties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'OS X'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;os_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'High Sierra'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;browserName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Safari'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;browser_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'11.1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'1920x1080'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'browserstack.safari.driver'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'2.48'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Win10_FF_720'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;commonCapabilityProperties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Windows'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;os_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'10'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;browserName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Firefox'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;browser_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'64.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'1920x1080'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;customWindowSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="s1"&gt;'browserstack.geckodriver'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'0.22.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'GalaxyS9_Chrome_landscape'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;commonCapabilityProperties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;os_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'8.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;device&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Samsung Galaxy S9'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;browserName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Chrome'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;browser_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'71.0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;real_mobile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;deviceOrientation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'landscape'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'browserstack.appium_version'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'1.9.1'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'IPhoneX_Safari_portrait'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;commonCapabilityProperties&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;device&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'iPhone X'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;browserName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Safari'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;real_mobile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'true'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'browserstack.appium_version'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'1.9.1'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;„CustomWindowSize“&lt;/strong&gt; is not a standard property. We use it if we do not want to run the tests in a maximized browser window, but in a window of a defined size. More about that right now.&lt;/p&gt;

&lt;h4&gt;
  
  
  Settings
&lt;/h4&gt;

&lt;p&gt;We now have to make some smaller settings.&lt;br&gt;&lt;br&gt;
The SeleniumAdresse:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;seleniumAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'http://hub-cloud.browserstack.com/wd/hub'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The maximum number of Browserstack instances used:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;maxSessions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Normally Browserstack instances can access the locally hosted instance via localhost. Unfortunately there are problems with this kind of redirection on the IOS devices. This has been resolved by redirecting to the local instance via „bs-local.com“.&lt;br&gt;&lt;br&gt;
Furthermore, there is a limitation in the possible ports that can be used. Information can be found on the Browserstack &lt;a href="https://www.browserstack.com/question/664"&gt;FAQs&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
In our example, the app is hosted locally on port 5000.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'http://bs-local.com:5000/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Access to the Screenshoter
&lt;/h4&gt;

&lt;p&gt;In order to be able to access the &lt;strong&gt;screenshoter&lt;/strong&gt; in the tests, we provide this as a protractor parameter via a function.&lt;br&gt;&lt;br&gt;
To do this, we create a global map and define the protractor parameters:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenshoters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;getScreenshoter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;screenshoters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Connection to Browserstack
&lt;/h4&gt;

&lt;p&gt;When starting Protractor we connect to the browser stack and propagate our session. In case of an error, the start should be aborted.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nx"&gt;beforeLaunch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Connecting local'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bs_local&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;browserstack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bs_local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;commonCapabilityProperties&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'browserstack.key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
        &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Connected to browserstack'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Before we finish Protractor, we close the connection properly.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;afterLaunch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bs_local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Before the environment is getting started…
&lt;/h4&gt;

&lt;p&gt;For each individual environment, a &lt;strong&gt;screenshoter&lt;/strong&gt; should be instantiated and the window should be maximized or adjusted.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;onPrepare&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;//...&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getProcessedConfig&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;screenshoters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; 
         &lt;span class="nx"&gt;Screenshoter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;resizeWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizeWindow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isDesktop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'Windows'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;os&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'OS X'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customWindowSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customWindowSize&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; 
     &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customWindowSize&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDesktop&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customWindowSize&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;manage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customWindowSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customWindowSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Resized window to '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;customWindowSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'x'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; 
         &lt;span class="nx"&gt;customWindowSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;driver&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;manage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maximize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Maximized window'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;switchTo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  After all tests of a single environment have been executed…
&lt;/h4&gt;

&lt;p&gt;After all tests have been executed, the captured screenshots a saved locally.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;onComplete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;screenshotDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/screenshots/`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getScreenshoter&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;saveScreenshots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screenshotDir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Specification of the semi-automated tests
&lt;/h3&gt;

&lt;p&gt;We have to write a test spec that puts the app in the desired state and finally captures the screenshot.&lt;br&gt;&lt;br&gt;
A spec for making a screenshot might look like this.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SomeResponsiveComponent'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'should display a responsive dialog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openResponsiveDialog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getScreenshoter&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;takeScreenshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ResponsiveDialog'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The screenshot is saved in the project´s subfolder „screenshots/ResponsiveDialog/.png“.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Browser stack can greatly facilitate and save costs during the development for multiple browsers and operating systems.&lt;/p&gt;

&lt;p&gt;By creating screenshots, working hours can be saved and feedback can be generated immediatly without manually and monotonously changing the app again and again in various browsers to a defined state.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="http://maimart.de/responsive-design-with-protractor-and-browserstack"&gt;Responsive Design with Protractor and Browserstack&lt;/a&gt; appeared first on &lt;a href="http://maimart.de"&gt;maimArt - Crafted Software&lt;/a&gt;.&lt;/p&gt;


</description>
      <category>webdev</category>
      <category>browserstack</category>
      <category>angular</category>
      <category>protractor</category>
    </item>
    <item>
      <title>Testgetriebene Entwicklung von Angular Apps (W-JAX 2018)</title>
      <dc:creator>Martin Maier</dc:creator>
      <pubDate>Wed, 07 Nov 2018 20:31:04 +0000</pubDate>
      <link>https://dev.to/_maimart_/testgetriebene-entwicklung-von-angular-apps-w-jax-2018-4m3n</link>
      <guid>https://dev.to/_maimart_/testgetriebene-entwicklung-von-angular-apps-w-jax-2018-4m3n</guid>
      <description>

&lt;p&gt;Hier befinden sich die Slides meines Vortrags über die testgetriebene Entwicklung von Angular Apps vom 6.11.2018 der WJAX.&lt;/p&gt;

&lt;p&gt;Das Projekt liegt auf &lt;a href="https://github.com/maimArt/tddangular"&gt;GitHub&lt;/a&gt;. Die einzelnen Entwicklungsschritte sind dort innerhalb der Commits und dem zugehörigen GitHub-Project dokumentiert.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://speakerdeck.com/maimart/testgetriebene-entwicklung-von-angular-apps-wjax-2018"&gt;https://speakerdeck.com/maimart/testgetriebene-entwicklung-von-angular-apps-wjax-2018&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="http://maimart.de/testgetriebene-entwicklung-von-angular-apps-w-jax-2018"&gt;Testgetriebene Entwicklung von Angular Apps (W-JAX 2018)&lt;/a&gt; appeared first on &lt;a href="http://maimart.de"&gt;maimArt&lt;/a&gt;.&lt;/p&gt;


</description>
      <category>angular</category>
      <category>tdd</category>
      <category>webdev</category>
      <category>ng</category>
    </item>
    <item>
      <title>Tutorial: Developing an angular app driven by tests</title>
      <dc:creator>Martin Maier</dc:creator>
      <pubDate>Mon, 30 Jul 2018 14:49:35 +0000</pubDate>
      <link>https://dev.to/_maimart_/tutorial-developing-an-angular-app-driven-by-tests-51ae</link>
      <guid>https://dev.to/_maimart_/tutorial-developing-an-angular-app-driven-by-tests-51ae</guid>
      <description>&lt;p&gt;This article is a tutorial for developing Angular applications driven by tests (TDD).&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For a long time graphical user interfaces of complex systems were made of solid and heavy weighted technologies of higher developed, object oriented programming languages.&lt;/p&gt;

&lt;p&gt;Mostly UIs have been fat clients, developed with WPF or Swing. Just rarely they were thin clients as web applications.&lt;/p&gt;

&lt;p&gt;And even in this seldom cases, HTML and JavaScript got abstracted by big frameworks like ASP or JSF and rendered on server side.&lt;/p&gt;

&lt;p&gt;But the language core of JavaScript evolved well with ECMAScript 2015 and so got interesting for more complex applications, too.&lt;/p&gt;

&lt;p&gt;Based on the suggestions to ECMAScript2015, TypeScript raised and became an additional option to build web applications on a very high language level that even types statically.&lt;/p&gt;

&lt;p&gt;This big language improvements and the strong support of giants like Facebook and Google made web technologies to get more and more interesting for developing more complex applications as single page applications (SPA).&lt;/p&gt;

&lt;p&gt;Unfortunately the source code of web applications still has the bad record to be on a lower quality level when regarding readability, design and test coverage.&lt;/p&gt;

&lt;p&gt;But the value of creating high quality code, for sustainable further development and maintenance, is independent of the technology and so are the methodologies that ensures that value.&lt;/p&gt;

&lt;p&gt;Test driven development (TDD) is a methodology of the Extreme Programming (XP) to develop high quality and well tested code.&lt;/p&gt;

&lt;p&gt;In TDD we first specify and validate the behavior in tests, before we start to implement it. When the tests succeeds, we refactor the code to improve the readability and structure of the code.&lt;/p&gt;

&lt;p&gt;This leads to well testable and tested code and a process where developers early have to think about the structure, respectively the design of the code.&lt;/p&gt;

&lt;p&gt;By specifying functionality in tests first,  we effectively only develop functionality that is required and preserves against developing predictive and unnecessary complex code.&lt;/p&gt;

&lt;p&gt;Projects created with the Angular CLI, out of the box contains all tools we need to develop driven by tests.&lt;/p&gt;

&lt;p&gt;This tutorial bases on a simple scenario and describes how to develop Angular project driven by tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;We want to focus the methodology and so choose a very simple scenario.&lt;/p&gt;

&lt;p&gt;The application is a very lightweight contact management tool and provides only two functionalities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;displaying contacts&lt;/li&gt;
&lt;li&gt;adding new contacts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup the Angular project
&lt;/h2&gt;

&lt;p&gt;We first create an Angualr project with the Angular CLI.&lt;br&gt;
&lt;code&gt;ng new ContactManager&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We want to start from the scratch, so we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;delete the component &lt;em&gt;app.component&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;cleanup dependents &lt;em&gt;app.module&lt;/em&gt; und &lt;em&gt;index.html&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;delete the end-to-end specifications in the directory &lt;em&gt;e2e/src&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;and check our changes by building the project with &lt;code&gt;ng build&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The „Walking Skeleton“
&lt;/h2&gt;

&lt;p&gt;Our system is very small, does not communicate with any backend and for the sake of convenience it is not deployed automatically.&lt;/p&gt;

&lt;p&gt;So in our case the walking skeleton is a crosscut for developing our application driven by tests.&lt;/p&gt;

&lt;p&gt;This should contain an e2e-test that interacts with the web application by page objects and a first small component which is developed driven by tests.&lt;/p&gt;

&lt;p&gt;A suitable first small story to build the walking skeleton is to display the contacts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Approach
&lt;/h2&gt;

&lt;p&gt;We develop in short TDD iterations.&lt;/p&gt;

&lt;p&gt;I am using some special text styles to get the tutorial more readable and clear:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;First we talk about and specify what we want to achieve in the next tdd &lt;br&gt;
iteration. In best case we don´t develop for our own, but have paired with &lt;br&gt;
another developer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test fails!&lt;/em&gt;&lt;/strong&gt; We specify an accordingly test, start it and watch it fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test succeeds!&lt;/em&gt;&lt;/strong&gt; After that we implement the functionality and start the test again until it succeeds.&lt;/p&gt;

&lt;p&gt;We check our code style, make some refactorings and after that we start with the next small tdd iteration.&lt;/p&gt;
&lt;h2&gt;
  
  
  Specifying the story in a first e2e-test
&lt;/h2&gt;

&lt;p&gt;We specify the context of the “contact management” in a new e2e-specification /e2e/src/c_ontact-management.e2e-spec.ts_.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We define, that the contact management initially lists the two contacts “Max” and “Moritz”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Within the e2e-test we want to interact with the application on a very high level and delegate the access to the HTML-Elements to so called “page objects”.&lt;/p&gt;

&lt;p&gt;In TDD manner, we write the e2e-test, as if there would already exist a class _ContactManagementPage _and so define it in the most meaningful way from the clients point of view.&lt;/p&gt;

&lt;p&gt;We create an instance of the &lt;em&gt;ContactManagementPage&lt;/em&gt;, navigate to its view and verify that the contacts “Max” and “Moritz” are listed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Contact Management&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should show a list of contacts, initially containing "Max" and "Moritz"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContactManagementPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ContactManagementPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigateTo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shownNamesOfContacts&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Max&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shownNamesOfContacts&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Moritz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When finished, we create the class &lt;em&gt;ContactManagementPage&lt;/em&gt; &lt;em&gt;/e2e/src/pageobjects/contact-management.po.ts,&lt;/em&gt; as assumed in the e2e-test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test fails!&lt;/em&gt;&lt;/strong&gt; Now the test compiles, but fails as expected.&lt;/p&gt;

&lt;p&gt;We start implementing the page object´s &lt;em&gt;navigateTo&lt;/em&gt;-method, which navigates to the contact management view. In our case it is the root content “/”.&lt;/p&gt;

&lt;p&gt;Next we need the names of all listed contacts. At this point, we define that the view contains HTML-elements of the class &lt;em&gt;“contact”,&lt;/em&gt; which again contains elements of the class &lt;em&gt;“name”&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We implement &lt;em&gt;#shownNamesOfContacts&lt;/em&gt; by getting these elements and map their text values.&lt;/p&gt;

&lt;p&gt;Protractor works asynchronous with its own &lt;em&gt;Promise&lt;/em&gt; class. We have to make sure to import and use the right  &lt;em&gt;Promise&lt;/em&gt;-Class of the Protractor library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ElementArrayFinder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ElementFinder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;protractor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ContactManagementPage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;navigateTo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;shownNamesOfContacts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contactNameItems&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactNameItem&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;contactNameItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;contactNameItems&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ElementArrayFinder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.contact .name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test fails!&lt;/em&gt;&lt;/strong&gt; The test fails with the hint that no Angular application could be found. Currently no angular component gets bootstrapped.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To fix that issue we next create an Angular component “&lt;em&gt;contact-list&lt;/em&gt;” with the Angular CLI.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng generate component contact-list&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It is automatically assigned to the global a_pp-module_ and declared within it. So far that´s ok for this tutorial, but we also have to integrate the component in our &lt;em&gt;index.html&lt;/em&gt; and configure our  module to bootstrap the component.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test fails!&lt;/em&gt;&lt;/strong&gt; The e2e-test still fails, but this time as intended by not fulfilling the expectations of the test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the component
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;First the component should display a list of items per contact.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We create a test case in the &lt;em&gt;contact-list.component.spec&lt;/em&gt; which was generated by the Angular CLI. While writing it, we again assume all dependencies already exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should display an element for each contact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DummyContact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DummyContact2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
  &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contactElements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NodeList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.contact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contactElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code does not compile. We need a data structure „&lt;em&gt;Contact&lt;/em&gt;“ and a property „&lt;em&gt;contacts“&lt;/em&gt; in the component´s controller &lt;em&gt;contact-list.components.ts&lt;/em&gt; which provides the contacts_._&lt;/p&gt;

&lt;p&gt;We create the class Klasse &lt;em&gt;/model/contact.ts&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and extend the controller by the property &lt;code&gt;contacts: Contact[]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test fails!&lt;/em&gt;&lt;/strong&gt; This time we start the karma test server and runner with &lt;code&gt;ng test&lt;/code&gt;and watch the written test failing, because we expect two contact elements that are currently no displayed.&lt;/p&gt;

&lt;p&gt;We keep karma running and so get instant feedback about our changes and their impact on the behavior.&lt;/p&gt;

&lt;p&gt;To display an item for each contact, we extend the view &lt;em&gt;contact-list.component.html&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let contact of contacts"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"contact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test succeeds!&lt;/em&gt;&lt;/strong&gt; The test succeeds!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So far, now we want to display the contact´s names.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a contact element should display the contact´s name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SomeName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contacts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nameElement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.contact .name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nameElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toBeNull&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nameElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test fails!&lt;/em&gt;&lt;/strong&gt; As expected the test fails.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We make the list to display the name.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;*ngFor=&lt;/span&gt;&lt;span class="s"&gt;"let contact of contacts"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"contact"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{contact.name}}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test succeeds!&lt;/em&gt;&lt;/strong&gt; Our component now displays the contact´s names.&lt;/p&gt;

&lt;p&gt;Are we finished? We have a look at the e2e-tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test fails!&lt;/em&gt;&lt;/strong&gt; They still fail. We expect “Max” and “Moritz” to be listed initially.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We want our component to initially contain the contacts “Max” and “Moritz”.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should initially display the contacts "Max" and "Moritz"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nameElements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NodeList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.contact .name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nameElements&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nameElement&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;nameElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Max&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Moritz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test fails!&lt;/em&gt;&lt;/strong&gt; Now we also have a failing component test.&lt;/p&gt;

&lt;p&gt;We initialize the controller´s contact-property with that contacts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test succeeds!&lt;/em&gt;&lt;/strong&gt; All component tests succeeds!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Test succeeds!&lt;/em&gt;&lt;/strong&gt; Now the e2e-test is also green and the implementation of the story is done!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Contact Management&lt;br&gt;&lt;br&gt;
√ should show a list of contacts, initially containing “Max” and “Moritz”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Further development
&lt;/h2&gt;

&lt;p&gt;Now our skeleton walks and we can continue with further development.&lt;/p&gt;

&lt;p&gt;Possible next steps could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactoring: Extraction and delegation of the contact-list-specific content to its own page object to keep the contact-page-object clean and to reuse it in other tests.&lt;/li&gt;
&lt;li&gt;Story: Creation of new contacts

&lt;ul&gt;
&lt;li&gt;Creating a new contact by entering its name in a textfield followed by pressing the enter-button&lt;/li&gt;
&lt;li&gt;when a new contact is created, the textfield should be cleared&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Optimization: Managing the data in a Redux-store&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Frameworks like Jasmine, Karma or Protractor support test driven development of web applications very well.&lt;/p&gt;

&lt;p&gt;In case of Angular, projects created by the Angular CLI are already preconfigured with Jasmin, Karma and Protractor and out of the box can be developed driven by tests.&lt;/p&gt;

&lt;p&gt;So TDD is also for web applications a very good methodology to create tested and well designed source code on a very high quality level.&lt;/p&gt;

&lt;p&gt;In combination with other methodologies like pair programming and clean coding, nice synergy effect arise to develop and maintain complex applications in an effective and efficient way.&lt;/p&gt;

&lt;p&gt;See also &lt;a href="http://maimart.de/test-strategies-when-developing-redux-stores-in-angular-apps-driven-by-tests"&gt;Test strategies when developing redux stores in angular apps driven by tests&lt;/a&gt; and &lt;a href="http://maimart.de/pushing-an-angular-project-to-the-aws-cloud"&gt;Pushing an angular project to the aws cloud&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="http://maimart.de/developing-an-angular-app-driven-by-tests"&gt;Tutorial: Developing an angular app driven by tests&lt;/a&gt; appeared first on &lt;a href="http://maimart.de"&gt;maimArt&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>tdd</category>
      <category>jasmine</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Test strategies when developing redux stores in angular apps driven by tests (reworked and extended)</title>
      <dc:creator>Martin Maier</dc:creator>
      <pubDate>Tue, 24 Jul 2018 10:04:59 +0000</pubDate>
      <link>https://dev.to/_maimart_/test-strategies-when-developing-redux-stores-in-angular-apps-driven-by-tests-2700</link>
      <guid>https://dev.to/_maimart_/test-strategies-when-developing-redux-stores-in-angular-apps-driven-by-tests-2700</guid>
      <description>

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CkFGw5vE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i0.wp.com/maimart.de/wp-content/uploads/2018/07/TDD__Angular.png%3Fw%3D1280" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CkFGw5vE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i0.wp.com/maimart.de/wp-content/uploads/2018/07/TDD__Angular.png%3Fw%3D1280" alt="TDD Angular"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I generally develop angular applications driven by tests.&lt;br&gt;
That works well, but when using redux in my projects (currently ngrx, but also angular/redux), there are several different aspects to consider when testing and developing stores and feature/fractal-stores, driven by tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intend
&lt;/h2&gt;

&lt;p&gt;The issue is to find a good strategy to test and develop driven by tests.&lt;/p&gt;

&lt;p&gt;There are e2e-tests, integration tests and unit tests, where last ones mostly tests static functions and static reactive variables in this context.&lt;/p&gt;

&lt;p&gt;My main intend is to drive the development by tests from an integration level of stores and substores, down to the single components of the stores.&lt;/p&gt;

&lt;p&gt;I want to be able to initially develop the store without integration in the rest of the angular application.&lt;/p&gt;

&lt;p&gt;So scenarios where migrating from a non-redux data management or migrating from one redux framework to another can be done smoothly without a big bang.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different perspectives and strategies
&lt;/h2&gt;

&lt;p&gt;I tried different strategies of different perspectives.&lt;/p&gt;

&lt;p&gt;When writing about store´s components i mean its reducers, effects, actions and action creators.&lt;/p&gt;

&lt;p&gt;For me selectors are not parts of a store, but rather convenience functions that helps to access subsets of its state.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Treat the store and its components as a whole
&lt;/h3&gt;

&lt;p&gt;When we treat the store´s components as just outsourced functions and variables of an holistic store, it is sufficient to test them integrated within encapsulating stores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; technical functionality of a store is specified in a whole&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; stores are explicitly tested as one integrated component, so their functionality is not just ensured in the e2e-tests&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; the direct effects and side effects of actions are specified at one single point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; encapsulation into a store is only fictive. In practice there is no explicit class for each store, there is just one big store object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; it is a very object orientated perspective in a more functional world&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; can lead to very comprehensive integration test suites&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; no “real” unit tests&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Treat the store´s components strictly as separate components that are tested isolated
&lt;/h3&gt;

&lt;p&gt;We treat the store´s components as separate and completely independent components and test them isolated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; small, lean test suites focusing on the respective functions and reactive variables&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; impact of actions is specified separately to the responsibility of the different components&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; real unit tests&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; the interaction of the different components is just tested in the e2e-tests&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; the specification of the actions´ impact is spread into multiple test suites&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; the high cohesion of the store´s components is only seen in the file structure and their configuration within the ng modules&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Treat the store´s components as separate components that are tested separate, but integrated.
&lt;/h3&gt;

&lt;p&gt;We treat the store´s components as separate components, test them also separately, but integrated in stores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; small, lean tests focusing on the respective functions and reactive variables&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; impact of actions is specified separated to the responsibility of the different components&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; components of stores are tested integrated, so their functionality is not just ensured in the e2e-tests&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; the belonging to a specific store is also seen in the integration tests&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; the specification of the actions´ impact is spread into multiple specs&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; on first look on the spec name seems so be unit tests, but are integration tests&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; no “real” unit tests&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Treat the store and its components as a whole that is developed and tested depending on its actions
&lt;/h3&gt;

&lt;p&gt;We treat the store and its components as a whole, develop and test them driven by its actions.&lt;/p&gt;

&lt;p&gt;This perspective is similar to the first one, but orientates a bit more at the core concept and idea of Redux and also enables a better substructuring.&lt;/p&gt;

&lt;p&gt;The thought is that in Redux and its stores is all about actions.&lt;/p&gt;

&lt;p&gt;Actions are fired to change data and to trigger side effects which in turn trigger services. All the behavior is driven by actions.&lt;/p&gt;

&lt;p&gt;So in this approach the tests are structured depending on the actions.  Whereby the actions are tested integrated in the store.&lt;/p&gt;

&lt;p&gt;In more complex stores, actions should be grouped to different kinds of actions and there should be one test suite for each group.&lt;/p&gt;

&lt;p&gt;Tests of actions with simple behavior, such as only manipulating the state, can be specified directly on the top level of the test suite.&lt;/p&gt;

&lt;p&gt;Tests of actions that leads to some more complex behavior should be substructured in nested test suites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; all impact of an action is specified and tested at one single point, not spread on the tests of the different technical components&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; technical functionality of a store is specified in a whole&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;+&lt;/strong&gt; stores are explicitly tested as one integrated component, so their functionality is not just ensured in the e2e-tests&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-&lt;/strong&gt; no "real" unit tests&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;First i started with a mix of the first and second approach, but soon the redundancy between the store´s integration test and the tests of its single components led me to hold on and to rethink about my perspective and this approach.&lt;/p&gt;

&lt;p&gt;My second thought was to treat the components of the stores as just outsourced functions and reactive variables and to only test them encapsulated in the integration tests anymore.&lt;/p&gt;

&lt;p&gt;Not to unit test the stores´ components still felt a bit uncommon, but the test driven development of the redux store now felt really good.&lt;/p&gt;

&lt;p&gt;Furthermore because of structuring the tests in nested suits for every action, the output of the tests was also really nice. I saw at a glance what effects and side effects every action triggers.&lt;/p&gt;

&lt;p&gt;But at least when i extended the store with multiple different kinds of side effects the integration test became much to big.&lt;/p&gt;

&lt;p&gt;The second approach i just evaluated in theory, because for TDD i missed the integration test and also this would end up in a big bang, especially in the migration scenarios.&lt;/p&gt;

&lt;p&gt;For some time the third approach felt as a good compromise and i think it is a good approach, but it still bothered me that the behavior of an action is spread on the different tests of the store´s components.&lt;/p&gt;

&lt;p&gt;The test driven development and readability of the tests still did not feel fluent and continuous.&lt;/p&gt;

&lt;p&gt;So finally i ended up with the last approach, where the development is driven by the actions and tests a structured depending on them. In Redux all is about the actions and in this approach the tests are too.&lt;/p&gt;

&lt;p&gt;I would be very glad to read about your opinions and approaches to this issue.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="http://maimart.de/test-strategies-when-developing-redux-stores-in-angular-apps-driven-by-tests"&gt;Test strategies when developing redux stores in angular apps driven by tests&lt;/a&gt; appeared first on &lt;a href="http://maimart.de"&gt;maimArt&lt;/a&gt;.&lt;/p&gt;


</description>
      <category>angular</category>
      <category>redux</category>
      <category>tdd</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Replicate immutable data in typescript with the ReplicationBuilder</title>
      <dc:creator>Martin Maier</dc:creator>
      <pubDate>Mon, 16 Jul 2018 14:33:00 +0000</pubDate>
      <link>https://dev.to/_maimart_/replicate-immutable-data-in-typescript-with-the-replicationbuilder-4ah3</link>
      <guid>https://dev.to/_maimart_/replicate-immutable-data-in-typescript-with-the-replicationbuilder-4ah3</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fmaimart.de%2Fwp-content%2Fuploads%2F2018%2F07%2FReplicationBuilder.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fmaimart.de%2Fwp-content%2Fuploads%2F2018%2F07%2FReplicationBuilder.gif" title="ReplicationBuilder Demo" alt="ReplicationBuilder Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ReplicationBuilder is a typescript tool to clone immutable data objects in a typesafe, refactorable and well readable way.&lt;/p&gt;

&lt;p&gt;In our single page applications we often use different implementations of redux to handle our application state.&lt;/p&gt;

&lt;p&gt;The more complex the data structures become, the more complex becomes the code that handles the immutable states when using Object.assign() or something like that.&lt;/p&gt;

&lt;p&gt;Therefore this often ends up in code that is really hard to read and maintain.&lt;/p&gt;

&lt;p&gt;So my intention was to create a tool that can handle immutable data in a typesafe, refactorable and well readable way.&lt;/p&gt;

&lt;p&gt;The powerful keyof-feature introduced in Typescript 2.1 to lookup for objects properties in a typesafe way helped me a lot to solve that.&lt;/p&gt;

&lt;p&gt;The result i called ReplicationBuilder and is a typesafe, fluent API to build replications of an object.&lt;/p&gt;

&lt;p&gt;Together with some other small helpers its published in the npm package "typescript-immutable-helper".&lt;/p&gt;

&lt;p&gt;The source code is hosted on &lt;a href="https://github.com/maimArt/typescript-immutable-helper" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would be glad for some feedback or if you want to constribute.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax
&lt;/h2&gt;

&lt;h4&gt;
  
  
  simply replace property by with new value
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ReplicationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;party&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyParty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  replace property depending on old value
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ReplicationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;party&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;partymemberArray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;oldPartymemberArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;oldPartymemberArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new partymember&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  clone property and apply some function on it
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ReplicationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;party&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;withCloneAndDo&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;clonedParty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;clonedParty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPartyMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new partymember&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Characteristics
&lt;/h2&gt;

&lt;h4&gt;
  
  
  typesafe properties
&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://maimart.de/wp-content/uploads/2018/07/typescript_immutable_helper_invalidProperty.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fmaimart.de%2Fwp-content%2Fuploads%2F2018%2F07%2Ftypescript_immutable_helper_invalidProperty.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  typesafe property values
&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://maimart.de/wp-content/uploads/2018/07/typescript_immutable_helper_invalidValue.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fmaimart.de%2Fwp-content%2Fuploads%2F2018%2F07%2Ftypescript_immutable_helper_invalidValue.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  chainable
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ReplicationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;party&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replaceValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyParty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replaceValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;members&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;by&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;members&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newMember&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;initiator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replaceValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prename&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Party&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replaceValueOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;surname&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;guy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  refactorable and easy to read
&lt;/h4&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;ol&gt;
    &lt;li&gt;Load an object by calling &lt;code&gt;ReplicationBuilder.forObject()&lt;/code&gt;
&lt;/li&gt;
    &lt;li&gt;Navigate down the object tree through the typesafe function &lt;code&gt;property()&lt;/code&gt;
&lt;/li&gt;
    &lt;li&gt;Modify a property with either (see syntax paragraph above)
&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;replaceValueOf('prop').with(newValue:T)&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;replaceValueOf('prop').by((T) =&amp;gt; newValue:T)&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;replaceValueOf('prop').withCloneAndDo((clonedProp) =&amp;gt; clonedProp.doSomething()&lt;/code&gt;&lt;/li&gt;
    &lt;li&gt;
&lt;code&gt;removeProperty('prop')&lt;/code&gt; to remove the property in the resulting object&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
    &lt;li&gt;Repeat step 3 and 4 until all modifications are done&lt;/li&gt;
    &lt;li&gt;Produce the replica with &lt;code&gt;build()&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;&lt;/h2&gt;

&lt;h2&gt;
&lt;a id="user-content-behaviour" href="https://github.com/maimArt/typescript-immutable-helper#behaviour" rel="noopener noreferrer"&gt;&lt;/a&gt;Behaviour&lt;/h2&gt;

&lt;ul&gt;
    &lt;li&gt;deep copies the source object&lt;/li&gt;
    &lt;li&gt;freeze in --&amp;gt; freeze out. If the source object was frozen (for detecting manipulations while development), then the produced replica will also be deep frozen.&lt;/li&gt;
    &lt;li&gt;warning if source object is not deep frozen (produced replica will be deep frozen)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&lt;/h2&gt;

&lt;h2&gt;Demo&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fmaimart.de%2Fwp-content%2Fuploads%2F2018%2F07%2FReplicationBuilder.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fmaimart.de%2Fwp-content%2Fuploads%2F2018%2F07%2FReplicationBuilder.gif" title="ReplicationBuilder Demo" alt="ReplicationBuilder Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;originally posted on &lt;a href="http://maimart.de" rel="noopener noreferrer"&gt;http://maimart.de&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>immutable</category>
      <category>assign</category>
      <category>clone</category>
    </item>
    <item>
      <title>Architecture of a JavaFX application</title>
      <dc:creator>Martin Maier</dc:creator>
      <pubDate>Sat, 24 Mar 2018 19:04:13 +0000</pubDate>
      <link>https://dev.to/_maimart_/architecture-of-a-javafx-application-2geb</link>
      <guid>https://dev.to/_maimart_/architecture-of-a-javafx-application-2geb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://maimart.de/about-me" rel="noopener noreferrer"&gt;I&lt;/a&gt; am developing JavaFX applications for about 5 years now and used different architecture patterns for it.&lt;/p&gt;

&lt;p&gt;First i started with MVC, then MVVM and lastly we used MVP passive view in my current project.&lt;/p&gt;

&lt;p&gt;The way how to apply the MVC pattern is described well in the JavaFX documentation, MVVM is described well in the documentation of the mvvmFX framework, but for the MVP Passive View i spent a lot of time reading different articles about it, but i didn´t find the way that feels 100% right for me.&lt;/p&gt;

&lt;p&gt;Futhermore i didn´t find a satisfying article about how to coordinate the different views built with the MVP pattern.&lt;/p&gt;

&lt;p&gt;So i evaluated different ways of creating a complex JavaFX UI with views following the MVP Passive View pattern and how to coordinate them.&lt;/p&gt;

&lt;p&gt;My focus was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to avoid bidirectional dependencies between view and presenter&lt;/li&gt;
&lt;li&gt;find the best way to manage the domain-specific properties for the views&lt;/li&gt;
&lt;li&gt;parent components attach child components&lt;/li&gt;
&lt;li&gt;workflow controllers access the components.&lt;/li&gt;
&lt;li&gt;how and where to comunicate with the backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So i came to following conclusion that feels good for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure of a ui component
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Simple ui component with few view property bindings
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://i0.wp.com/maimart.de/wp-content/uploads/2018/03/Some-Component__Simple-Component_0.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhaeb7g94svrceq1qk4gh.png" width="700" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Component Module
&lt;/h5&gt;

&lt;p&gt;The whole component is bound in a context that manages the dependencies. I used guice so there is a private guice module that manages the internal dependencies.&lt;/p&gt;

&lt;p&gt;More global modules that use this module have to ensure that all dependencies needed by the presenter are provided.&lt;/p&gt;

&lt;h5&gt;
  
  
  Presenter
&lt;/h5&gt;

&lt;p&gt;The main purpose of the presenter is to provide domain-specific properties the view can bind on and to provide methods the view can straight delegate to.&lt;/p&gt;

&lt;p&gt;It is responsible &lt;strong&gt;what&lt;/strong&gt; information is shown, not how.&lt;/p&gt;

&lt;p&gt;The presenter must not hold business logic, only logic about &lt;strong&gt;adapting&lt;/strong&gt; between view and business logic.&lt;/p&gt;

&lt;p&gt;The presenter does not know the view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Presenter { private final StringProperty someString = new SimpleStringProperty(); void doSomething() { // not implemented yet } StringProperty someStringProperty() { return someString; } }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  View
&lt;/h5&gt;

&lt;p&gt;The view is responsible for &lt;strong&gt;how&lt;/strong&gt; information is shown.&lt;/p&gt;

&lt;p&gt;The view is am humble object. It does only three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provide a package-private factory method&lt;/li&gt;
&lt;li&gt;bind the properties of the controls to the presenter´s properties and delegates the control´s actions to the presenter within the initialize method&lt;/li&gt;
&lt;li&gt;provide its root node by a public methods
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class View { @FXML private StackPane root; @FXML private Label label; @FXML private Button button; private final Presenter presenter; private View(Presenter presenter) { this.presenter = presenter; } static View createInstance(Presenter presenter) { View view = new View(presenter); FXMLLoader.load("....", view); return view; } @FXML void initialize(){ label.textProperty().bind(presenter.someStringProperty()); button.setOnAction(event -\&amp;gt; presenter.doSomething()); } public Node getRootNode() { return root; } }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  More complex ui component with several view property bindings
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://i0.wp.com/maimart.de/wp-content/uploads/2018/03/Some-Component__Complex-Component_1.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx1yavldl0una4j8dkuz.png" width="700" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To keep the presenter clean, we outsource the view properties to a kind of view model.&lt;/p&gt;

&lt;p&gt;The view model is only a helper class for the view and the presenter, so its access level only needs to be package-private.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested ui components
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Nest a ui component as a child ui component (1:1 nesting)
&lt;/h4&gt;

&lt;p&gt;If you want to nest a child view you need to install its module in the parent´s module.&lt;/p&gt;

&lt;p&gt;So you can simply inject the exposed view of the child component into the factory method of the parent´s view.&lt;/p&gt;

&lt;p&gt;If necessary it´s presenter can directly injected in the parent´s presenter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i2.wp.com/maimart.de/wp-content/uploads/2018/03/ui-component-nest-another-ui-component__ui-component-nest-another-ui-component_2.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvb9sjla5bcydvnmvsm3.png" width="700" height="633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Nest a component as a kind of control (1:n nesting)
&lt;/h4&gt;

&lt;p&gt;Basicly i think a custom control should not be treated as a view and so not be implemented with the MVP pattern.&lt;/p&gt;

&lt;p&gt;There would be a solution to handle that case this way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the presenter of such a “control component” must be accessed through the view itself, not exposed by the module&lt;/li&gt;
&lt;li&gt;the module´s provider of the view must be injected into the parent view&lt;/li&gt;
&lt;li&gt;the parent view must care about instanciating child views/controls and to propagate it to its own presenter, that connects view logic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But as you see the MVP pattern is inappropriate for this typical use case of injecting custom control, so i won´t dive deeper into this case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coordination of the ui components
&lt;/h2&gt;

&lt;p&gt;A user always has a reason for using an interface of a system. So applications base on use cases.&lt;/p&gt;

&lt;p&gt;The realization of those uses cases are projected in some abstract objects that i call workflow controller.&lt;/p&gt;

&lt;p&gt;There are different concepts and names for it, but in common they build the context of a use case or workflow and there purpose is to keep control of the workflow on a very abstract level.&lt;/p&gt;

&lt;p&gt;In the ui layer their responsibility is to coordinate and orchestrate ui components, to build a context in which they interact with each other and how they interact with the backend.&lt;/p&gt;

&lt;p&gt;Depending on the complexity of the ui and the use cases, in many cases they will not control the ui components directly but abstracted by some helping components of different responsibilities.&lt;/p&gt;

&lt;p&gt;To keep the worklow controller decoupled from the presenter, the access to the presenters should be abstracted by a functional interface that is implemented by the presenter and used by the workflow controler.&lt;/p&gt;

&lt;p&gt;The callback from the presenter to the workflow controller can be implemented either by injecting a callback function to the presenter, attaching listers to its properties or letting the presenter fire events that are catched by the workflow controller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i1.wp.com/maimart.de/wp-content/uploads/2018/03/Workflow-controls-ui-components__Workflow-controls-ui-components_3.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2bpd17110kj7a0vrtthx.png" width="700" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another idea would be to handle the communication between the worklow controller and presenter through a task specific model that holds properties and callback functions.&lt;/p&gt;

&lt;p&gt;But in my opinion an interface is more object orientated and it is much harder to keep a model clean and independent from the workflow and the presenter stuff than an interface.&lt;/p&gt;

&lt;p&gt;So the presenter from above could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Presenter implements WorkflowTask{ private final StringProperty someString = new SimpleStringProperty(); private Consumer\&amp;lt;String\&amp;gt; onDoSomething; @Override public void showSomeString(String string) { someString.set(string); } @Override public void setOnDoSomething(Consumer\&amp;lt;String\&amp;gt; onDoSomething) { this.onDoSomething = onDoSomething; } void doSomething() { onDoSomething.accept(someString.get()); } StringProperty someStringProperty() { return someString; } }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interaction with the backend
&lt;/h2&gt;

&lt;p&gt;The workflow controllers treat the interaction with the the backend.&lt;/p&gt;

&lt;p&gt;For reasons of testability i prefer to use a service class that internally delegates to a fx service and returns a CompletableFuture to its clients.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i0.wp.com/maimart.de/wp-content/uploads/2018/03/Connecting-the-backend.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqgj8wvljnugiae9a9a2.png" width="700" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are interessted in Angular and AWS read my article about &lt;a href="http://maimart.de/pushing-an-angular-project-to-the-aws-cloud" rel="noopener noreferrer"&gt;how to push an angular spa to the cloud&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://maimart.de/privacy-policy" rel="noopener noreferrer"&gt;.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="http://maimart.de/javafx-architecture-and-design" rel="noopener noreferrer"&gt;Architecture of a JavaFX application&lt;/a&gt; appeared first on &lt;a href="http://maimart.de" rel="noopener noreferrer"&gt;maimArt&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>java</category>
      <category>javafx</category>
    </item>
  </channel>
</rss>
