<?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: Samuel Abada</title>
    <description>The latest articles on DEV Community by Samuel Abada (@mastersam07).</description>
    <link>https://dev.to/mastersam07</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%2F345227%2F4e6617dd-302e-471e-a0c1-78fd6175fd51.png</url>
      <title>DEV Community: Samuel Abada</title>
      <link>https://dev.to/mastersam07</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mastersam07"/>
    <language>en</language>
    <item>
      <title>Optimizing Flutter Test Execution Time: A Comprehensive Guide</title>
      <dc:creator>Samuel Abada</dc:creator>
      <pubDate>Wed, 10 Apr 2024 15:11:18 +0000</pubDate>
      <link>https://dev.to/mastersam07/optimizing-flutter-test-execution-time-a-comprehensive-guide-1c54</link>
      <guid>https://dev.to/mastersam07/optimizing-flutter-test-execution-time-a-comprehensive-guide-1c54</guid>
      <description>&lt;p&gt;Testing is a critical phase in the software development lifecycle, ensuring that your application meets its quality standards. However, as a project grows, so does its test suite, potentially leading to longer test execution times. For Flutter projects, there are effective strategies to optimize these times, ensuring efficient and rapid testing processes that fit seamlessly into your development workflow. This article explores practical approaches to cutting down Flutter test execution time and coverage optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Challenge
&lt;/h2&gt;

&lt;p&gt;Flutter's testing framework is robust, allowing developers to write unit, widget, and integration tests to cover various aspects of their applications. However, with the expansion of test suites, developers often face increased execution times, particularly when running tests with coverage collection enabled. This not only slows down the CI/CD pipeline but can also hamper developer productivity, especially in agile environments where quick feedback loops are crucial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies for Optimization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Parallel Test Execution/Concurrency
&lt;/h3&gt;

&lt;p&gt;One of the first strategies to consider is concurrency. By running tests concurrently, you can significantly reduce the overall time it takes to run your entire test suite. Flutter supports this through the &lt;code&gt;--concurrency&lt;/code&gt; flag in the flutter test command, allowing you to specify the number of concurrent tests processes to run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--concurrency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't mean each individual test function is executed in parallel with others within the same test file. Rather, it allows multiple test files to be run at the same time, each in its own Dart VM process. However, the actual performance gain depends on your machine's capabilities and the nature of the tests. &lt;strong&gt;Note&lt;/strong&gt;: The &lt;code&gt;--concurrency&lt;/code&gt; flag is ignored for integration tests because they often require a more controlled environment, potentially involving real or simulated devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Wrapper Test Method/Test Bundling
&lt;/h3&gt;

&lt;p&gt;A more nuanced approach involves grouping your tests into a single test suite, significantly reducing the performance penalty associated with coverage collection. This method involves creating a "wrapper" test file that imports and runs all individual tests. The advantage here is the reduction in the overhead of initializing the Dart VM for each test file and the optimization of coverage data collection.&lt;/p&gt;

&lt;h4&gt;
  
  
  Implementing the Wrapper Test
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a Wrapper Test File: Place a new Dart file in your project's test directory. Name it to reflect its purpose, such as &lt;code&gt;all_tests_wrapper.dart&lt;/code&gt;. The naming however, is irrelevant.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Group Your Tests: Import your individual test files and use the group function from flutter_test to encapsulate them within the main function of your wrapper test file.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example: all_tests_wrapper.dart&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_test/flutter_test.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'widget_tests.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;widget_tests&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'integration_tests.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;integration_tests&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Widget Tests'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;widget_tests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Integration Tests'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;integration_tests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;main&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;ul&gt;
&lt;li&gt;Run the Wrapper Test: Execute your tests through the wrapper to benefit from optimized test execution and coverage collection.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--coverage&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/all_tests_wrapper.dart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Sharding
&lt;/h3&gt;

&lt;p&gt;While parallel execution increases efficiency, test sharding takes it a step further by splitting your test suite into smaller segments (shards) and running each segment in parallel. This can be particularly effective in CI/CD environments where multiple executors or containers are available to run tests concurrently.&lt;/p&gt;

&lt;p&gt;Flutter's built-in sharding mechanism divides the test suite into multiple shards and allows each shard to be run independently. This is achieved by specifying the total number of shards and the index of the current shard being run.&lt;/p&gt;

&lt;h4&gt;
  
  
  Implementing Test Sharding in CI/CD
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Determine Total Shards
&lt;/h5&gt;

&lt;p&gt;Decide on the total number of shards you want to split your tests into. This could be based on the total number of tests, the structure of your project, or the resources available in your CI/CD environment.&lt;/p&gt;

&lt;h5&gt;
  
  
  Modify CI/CD Pipeline
&lt;/h5&gt;

&lt;p&gt;Adjust your CI/CD pipeline to run the test command multiple times, each time with a different &lt;code&gt;--shard-index&lt;/code&gt;, ranging from 0 to &lt;code&gt;--total-shards&lt;/code&gt; minus one.&lt;/p&gt;

&lt;h5&gt;
  
  
  CI/CD Pipeline Integration (GitHub Actions Example)
&lt;/h5&gt;

&lt;p&gt;When integrating test sharding into a CI/CD pipeline like GitHub Actions, you can leverage the matrix strategy to parallelize test execution across multiple runners, each running a different shard of tests as a separate job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI with Test Sharding and Coverage&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;shard-index&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# Define the number of shards here&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subosito/flutter-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;flutter-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.19.x'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests in Shards&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter test --coverage --total-shards=4 --shard-index=${{ matrix.shard-index }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration sets up four separate jobs, each running a portion of the tests. The &lt;code&gt;--total-shards=4&lt;/code&gt; flag indicates the tests are divided into four shards, and &lt;code&gt;--shard-index&lt;/code&gt; specifies the index of the shard to run in each job. You should ensure to merge the coverage reports before using the report for your needs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Caution: When to Use Test Sharding
&lt;/h4&gt;

&lt;p&gt;While test sharding is a powerful tool for optimizing test execution times in Flutter projects, it's essential to apply it judiciously. Here are key considerations to keep in mind:&lt;/p&gt;

&lt;h5&gt;
  
  
  Overhead of Initialization
&lt;/h5&gt;

&lt;p&gt;Each test shard runs in its own separate process, requiring a separate initialization phase. This includes starting up the Flutter test environment, which can introduce overhead, especially if the number of tests in each shard is relatively small.&lt;/p&gt;

&lt;h5&gt;
  
  
  CI/CD Resource Utilization
&lt;/h5&gt;

&lt;p&gt;While sharding can parallelize test execution, each shard typically runs on its own runner or container in CI/CD environments. If your CI/CD platform has limited parallel jobs or if the setup and teardown times for each job are significant, you might not see the expected decrease in total test time. In fact, for smaller test suites, the overhead of managing multiple parallel jobs might result in longer overall execution times compared to running all tests in a single job.&lt;/p&gt;

&lt;h4&gt;
  
  
  When to Consider Sharding
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Large Test Suites: For projects with a substantial number of tests, where running the entire suite in a single job becomes prohibitively time-consuming.&lt;/li&gt;
&lt;li&gt;Sufficient CI/CD Resources: If your CI/CD platform supports a high number of parallel jobs and the overhead of setting up additional jobs is minimal.&lt;/li&gt;
&lt;li&gt;Balanced Shard Distribution: When you can evenly distribute tests across shards to ensure that no single shard becomes a bottleneck.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Best Practices for Implementing Test Sharding
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Evaluate Your Needs: Start by analyzing your current test execution times and CI/CD resource availability. Implement sharding incrementally, beginning with a small number of shards to measure the impact on total execution time.&lt;/li&gt;
&lt;li&gt;Monitor and Adjust: Continuously monitor the performance of your test execution times after implementing sharding. Be prepared to adjust the number of shards based on the results and any changes in your test suite or CI/CD environment.&lt;/li&gt;
&lt;li&gt;Balance Test Distribution: Aim for an even distribution of tests across shards to avoid imbalances that could lead to some shards finishing much earlier than others, thereby not fully utilizing parallel execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test sharding in Flutter can be a double-edged sword. While it offers the potential for significantly reduced test execution times, especially for large and complex projects, it can also introduce overhead that may outweigh the benefits for smaller projects or in resource-constrained CI/CD environments. Careful consideration and continuous monitoring are key to leveraging test sharding effectively. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://pub.dev/packages/very_good_cli"&gt;Very Good Cli&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The Very Good CLI, developed by Very Good Ventures, introduces an innovative approach to optimizing Flutter test execution. The CLI's very_good test command supercharges Dart and Flutter apps testing with performance optimizations and developer experience improvements.&lt;/p&gt;

&lt;p&gt;Integrating Very Good CLI into your development workflow is straightforward. Install it globally via Dart, and use the very_good test command to run your tests. The command's flags and arguments, such as --coverage, --recursive, and --min-coverage, provide granular control over how your tests are executed and how coverage is collected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Very Good CLI&lt;/span&gt;
dart pub global activate very_good_cli

&lt;span class="c"&gt;# Run optimized tests with Very Good CLI&lt;/span&gt;
very_good &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--coverage&lt;/span&gt; &lt;span class="nt"&gt;--min-coverage&lt;/span&gt; 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CI/CD Integration
&lt;/h3&gt;

&lt;p&gt;Integrating these strategies into your CI/CD pipeline can further enhance efficiency. Use CI/CD capabilities to leverage parallel execution and test sharding, and consider the wrapper test method for large, complex projects that suffer from significant delays in test execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarking and Comparison
&lt;/h2&gt;

&lt;p&gt;To truly appreciate the impact of these optimization strategies on Flutter test execution times, it's beneficial to conduct a benchmarking exercise. This section compares regular flutter test execution times against optimized runs using the strategies discussed above, including running tests with coverage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark Setup
&lt;/h3&gt;

&lt;p&gt;To ensure a fair comparison, tests should be run under similar conditions and on the same hardware. For the purpose of this benchmark, we'll consider some Flutter project with a mix of unit and widget tests distributed across several features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Standard Flutter Tests
&lt;/h3&gt;

&lt;p&gt;First, run the entire test suite using the standard flutter test command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;time &lt;/span&gt;flutter &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, run the tests again with coverage enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;time &lt;/span&gt;flutter &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--coverage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record the total execution time for each run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6jp180huuobp403twn48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6jp180huuobp403twn48.png" alt="Flutter test on github actions" width="800" height="92"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp08oqo81zu96qil8zohg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp08oqo81zu96qil8zohg.png" alt="Flutter test on local machine" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Optimized Tests
&lt;/h3&gt;

&lt;p&gt;Next, apply the optimization strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel Test Execution: Use the &lt;code&gt;--concurrency&lt;/code&gt; flag with an appropriate value based on your machine's CPU cores.&lt;/li&gt;
&lt;li&gt;The Wrapper Test Method: Combine all tests into a single suite using a wrapper test file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the optimized tests and the optimized tests with coverage, recording the execution time for each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For The Wrapper Test Method&lt;/span&gt;
&lt;span class="nb"&gt;time &lt;/span&gt;flutter &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--coverage&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/all_tests_wrapper.dart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9vt4d2daxjuljkhpryd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9vt4d2daxjuljkhpryd.png" alt="Optimized flutter test on gihtub actions" width="800" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6u69lncnmya3xyxw99e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm6u69lncnmya3xyxw99e.png" alt="Optimized flutter test on local machine" width="800" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Results and Comparison
&lt;/h3&gt;

&lt;p&gt;After running the tests using both standard and optimized methods, we compare the execution times. Its observed that there is significant reductions in total test runtime, especially for the optimized tests with coverage. The exact performance gains will depend on factors like the number and complexity of tests, project size, and your CI/CD environment's capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimized Tests with Coverage vs. Flutter Test with Coverage
&lt;/h3&gt;

&lt;p&gt;When comparing optimized tests with coverage against standard flutter test runs with coverage, the difference can be stark. Coverage collection often adds considerable overhead, but by aggregating tests and minimizing the initialization overhead, the optimized approach can drastically reduce this penalty, resulting in faster CI/CD pipelines and more efficient development cycles.&lt;/p&gt;

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

&lt;p&gt;Optimizing test execution time in Flutter projects is essential for maintaining a fast, efficient development pipeline. Through parallel execution, test sharding, and innovative approaches like the wrapper test method, developers can significantly reduce testing times. This not only accelerates the CI/CD process but also improves developer productivity by providing quicker feedback on the quality of the codebase. Implementing these strategies ensures that your Flutter project remains agile, robust, and ready for rapid iteration.&lt;/p&gt;

&lt;p&gt;The goal of optimization is to find the sweet spot where the benefits of reduced execution time outweigh the costs of increased complexity and resource usage. &lt;/p&gt;

&lt;p&gt;Happy testing 🧪 &lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/flutter/flutter/issues/86722#issuecomment-914688482"&gt;flutter/flutter issue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/very_good_cli"&gt;Very good cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://verygood.ventures/blog/flutter-tests-very-good-cli"&gt;Supercharge your Flutter tests with Very Good CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>tutorial</category>
      <category>testing</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Test-Driven Development In Flutter</title>
      <dc:creator>Samuel Abada</dc:creator>
      <pubDate>Tue, 25 Feb 2020 18:31:49 +0000</pubDate>
      <link>https://dev.to/mastersam07/test-driven-development-in-flutter-35gl</link>
      <guid>https://dev.to/mastersam07/test-driven-development-in-flutter-35gl</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b0-m5h4d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/900/1%2AN2BXwOQSUtG8FegnyudvuA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b0-m5h4d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/900/1%2AN2BXwOQSUtG8FegnyudvuA.png" alt="Test Driven Development Flutter" width="800" height="387"&gt;&lt;/a&gt;Test Driven Development Flutter&lt;/p&gt;

&lt;p&gt;Test Driven Development(TDD), is a discipline in software development that prohibits us from writing our code/implementation before writing the test first.&lt;/p&gt;

&lt;p&gt;According to Uncle Bob, there are 3 rules for TDD:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;1. You are not allowed to write any production code unless it is to make a failing unit test pass.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;_3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uncle Bob (_ &lt;a href="http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd"&gt;&lt;em&gt;http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd&lt;/em&gt;&lt;/a&gt;&lt;em&gt;)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is a cycle we have to iterate over. We write a test on something that we want to implement such that when the test is run, the result will be “fail” then we write an implementation that is &lt;strong&gt;just enough&lt;/strong&gt; so that the test pass. We then fix our code quality which we may have overlooked since we were focusing on making the test pass without making the test fail.&lt;/p&gt;

&lt;p&gt;Some try to write the implementation first, the test later and then committing only the test. This is now how one should go about TDD. You should note that you are only allowed to implement just enough code so that your test pass(rule 3 according to Uncle Bob).&lt;/p&gt;

&lt;h3&gt;
  
  
  TDD In Flutter
&lt;/h3&gt;

&lt;p&gt;To understand this concept in Flutter, let’s jump into a practical example. For the purpose of this tutorial, I will be using the PiggyX application. The codes are available on Github. Click &lt;a href="https://github.com/Mastersam07/PiggyX"&gt;here&lt;/a&gt; to access the codes.&lt;/p&gt;

&lt;p&gt;We would create and test a couple of simple functionalities: EmailFieldValidator , PasswordFieldValidator . Let’s separate the login logic by creating a new file call loginpage.dart and implement as such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class EmailFieldValidator {
  static String _validate_(String value) {
    return false;
  }
}

class PasswordFieldValidator {
  static String _validate_(String value) {
    return false;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s write tests for the above validators. We head over to the test folder and delete the defaultwidget_tests.dart and add a new file called fieldvalidators_tests.dart . We implement the test as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:PiggyX/ui/loginpage.dart';
import 'package:flutter\_test/flutter\_test.dart';

void main() {

  test('empty email returns error string', () {

    final result = EmailFieldValidator._validate_('');
    expect(result, 'Email can\'t be empty');
  });

  test('non-empty email returns null', () {

    final result = EmailFieldValidator._validate_('email');
    expect(result, null);
  });

  test('empty password returns error string', () {

    final result = PasswordFieldValidator._validate_('');
    expect(result, 'Password can\'t be empty');
  });

  test('non-empty password returns null', () {

    final result = PasswordFieldValidator._validate_('password');
    expect(result, null);
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above, we expect the following cases:&lt;br&gt;&lt;br&gt;
i) The email field must not be empty&lt;br&gt;&lt;br&gt;
ii) The password field must not be empty&lt;br&gt;&lt;br&gt;
We can extend the test cases if we want to but for the purpose of this tutorial and this &lt;a href="https://github.com/Mastersam07/PiggyX"&gt;app&lt;/a&gt;, we would be sticking with these two.&lt;/p&gt;

&lt;p&gt;When we try running flutter test before the implementation and you might see +0 -4: Some tests failed.This tells us that 0 test passes (+) and 4 tests have failed (-)😪 😪 😪.&lt;/p&gt;

&lt;p&gt;Let’s fix them now in our TDD mode!&lt;/p&gt;

&lt;p&gt;Head over to loginpage.dart and fix EmailFieldValidator with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static String _validate_(String value) {
  return value.isEmpty ? 'Email can\'t be empty' : null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the test again and you should see +2 -2 .&lt;/p&gt;

&lt;p&gt;Now let’s head over to loginpage.dart and fix PasswordFieldValidator with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static String _validate_(String value) {
  return value.isEmpty ? 'Password can\'t be empty' : null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the tests again, you should +4: All Tests Passed! 💃 💃 💃&lt;/p&gt;

&lt;p&gt;You have successfully tested your app in a TDD manner with Flutter! You deserve some accolades. Now, it’s safe to complete our cycle since the objective of our feature is now completed (only making the UI)!&lt;/p&gt;

&lt;p&gt;The codes are available in this GitHub repo &lt;a href="https://github.com/Mastersam07/PiggyX"&gt;PiggyX&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus Points&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
For mocking dependencies, you can make use of the &lt;a href="https://pub.dev/packages/mockito"&gt;mockito package&lt;/a&gt;. For example, we need to be able to mock data from an app needs in order to authenticate against an API instead of relying on the live API, emulate a live web service or database to return specific results depending on the situation, operations with firebase.e.t.c. All we need to do is to create an alternate implementation of a class by either hand-coding or using the &lt;a href="https://pub.dev/packages/mockito"&gt;mockito package&lt;/a&gt;. You could follow this guide on &lt;a href="https://flutter.dev/docs/cookbook/testing/unit/mocking"&gt;mocking&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions, feel free to leave a comment 🙂.&lt;/p&gt;

&lt;h3&gt;
  
  
  References:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://flutter.dev/docs/cookbook/testing"&gt;Flutter Testing&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://flutter.dev/docs/testing"&gt;Testing Flutter Apps&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://flutter.dev/docs/cookbook/testing/unit/mocking"&gt;Mocking Dependencies Using Mockito&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://pub.dev/packages/mockito"&gt;Mockito Package&lt;/a&gt;&lt;/p&gt;




</description>
      <category>testing</category>
      <category>flutter</category>
      <category>ios</category>
      <category>android</category>
    </item>
    <item>
      <title>Configuring Travis CI and Coveralls with Flutter</title>
      <dc:creator>Samuel Abada</dc:creator>
      <pubDate>Mon, 24 Feb 2020 06:20:10 +0000</pubDate>
      <link>https://dev.to/mastersam07/configuring-travis-ci-and-coveralls-with-flutter-2b29</link>
      <guid>https://dev.to/mastersam07/configuring-travis-ci-and-coveralls-with-flutter-2b29</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3VqswlGI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AsthBZGJCvvVoRB22scLDwg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3VqswlGI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AsthBZGJCvvVoRB22scLDwg.png" alt="" width="800" height="493"&gt;&lt;/a&gt;Flutter et Travis CI&lt;/p&gt;

&lt;p&gt;In my previous article, I talked about test-driven development(TDD) with Flutter which you can read &lt;a href="https://medium.com/@sammytech/test-driven-development-in-flutter-e7fe7921ea92"&gt;here&lt;/a&gt;. This time around, we would try to set up for &lt;a href="https://travis-ci.com/"&gt;Travis-CI&lt;/a&gt; and integrate Flutter app with &lt;a href="https://coveralls.io/"&gt;Coveralls&lt;/a&gt;, which will give us feedback on test coverage in your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I hope this guide helps you out ** 😀 **.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Requirements&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The Flutter app should be hosted on GitHub, GitLab or Bitbucket.&lt;/li&gt;
&lt;li&gt;Travis CI account&lt;/li&gt;
&lt;li&gt;Flutter SDK&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create a Github/BitBucket/GitLab Project
&lt;/h3&gt;

&lt;p&gt;Create your flutter application and push it to GitHub/BitBucket/GitLab. For the purpose of this tutorial, I will be using the PiggyX application which is to demonstrate the concept of test-driven development in Flutter. The codes are available on Github. Click &lt;a href="https://github.com/Mastersam07/PiggyX"&gt;here&lt;/a&gt; to access the codes. You can read the previous article on test-driven development &lt;a href="https://medium.com/@sammytech/test-driven-development-in-flutter-e7fe7921ea92"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating with Travis CI
&lt;/h3&gt;

&lt;p&gt;You need to first register your GitHub project/repository on Travis-CI dashboard. This way, travis can detect changes in your repository. Go to &lt;a href="https://travis-ci.org/"&gt;https://travis-ci.org/&lt;/a&gt; if your repository is public, or &lt;a href="https://travis-ci.com/"&gt;https://travis-ci.com/&lt;/a&gt; if it is private.&lt;/p&gt;

&lt;p&gt;In Travis, you implement the pipeline under the &lt;em&gt;YAML&lt;/em&gt; schema, defined on a file named .travis.yml. To get things going on Travis all you need is add the correct .travis.yml to the root of your repository and tell Travis to start a continuous build of your repository by following &lt;a href="https://docs.travis-ci.com/user/getting-started"&gt;their instructions&lt;/a&gt;. Here I’ll only explain what goes in the .travis.yml file. Below is the configuration I settled on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;language: dart
dist: xenial
addons:
  apt:
    packages:
      - lib32stdc++6
install:
  - git clone https://github.com/flutter/flutter.git -b stable
  - ./flutter/bin/flutter doctor
script:
  - ./flutter/bin/flutter test
cache:
  directories:
    - $HOME/.pub-cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This tailors the CI environment for a specific language. Leaving out this part tells travis to use the default language &lt;strong&gt;Ruby&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Dist&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This specifies the Ubuntu base version that gets used. I’m specifying Xenial Xerus which is supported until 2021.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Addons&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This lets you have additional packages installed on the OS. In my experimentation, tests still pass without lib32stdc++6 but flutter doctor complained so I'm including it.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Install&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This should call Dart’s pub get but Flutter complains so let’s just override that and install Flutter instead.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Script&lt;/strong&gt; This is where we run the tests. So we provide the means of running the test here.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cache&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Here is the final part. This tells Travis to cache the contents of the default Dart package cache. This should make it faster for Flutter to install the app’s dependencies.  &lt;/p&gt;

&lt;p&gt;This configuration runs well and takes under 2minutes 30seconds to complete.&lt;/p&gt;
&lt;h3&gt;
  
  
  Adding Github Project to Coveralls
&lt;/h3&gt;

&lt;p&gt;To add a project to Coveralls, logon to &lt;a href="https://coveralls.io/"&gt;https://coveralls.io/&lt;/a&gt; and Sign In with your GitHub/GitLab/BitBucket account, go to &lt;em&gt;Add Repos&lt;/em&gt; and pick repository you would like to integrate. Then, when you go to &lt;em&gt;Repos&lt;/em&gt;, you should see your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hM_oiqJd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ANEmajRaktLEq3NRr4JQ-1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hM_oiqJd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ANEmajRaktLEq3NRr4JQ-1w.png" alt="" width="800" height="429"&gt;&lt;/a&gt;&lt;a href="https://coveralls.io/"&gt;coveralls.io&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Updating .travis.yml
&lt;/h3&gt;

&lt;p&gt;To call coveralls on Travis build you need to change .travis.yml file. All we need to do is tweak it a little to generate and send coverage report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;language: dart
dist: xenial
addons:
  apt:
    packages:
      - lib32stdc++6
install:
  - git clone https://github.com/flutter/flutter.git -b stable
  - ./flutter/bin/flutter doctor
  - gem install coveralls-lcov
script:
  - ./flutter/bin/flutter test --coverage
after\_success:
  - coveralls-lcov coverage/lcov.info
cache:
  directories:
    - $HOME/.pub-cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added two more lines and edited the script:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Install&lt;/strong&gt; : we added &lt;em&gt;gem install coveralls-lcov to install coveralls&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;Script&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;: we changed the script to ./flutter/bin/flutter test — coverage to generate coverage report on tests.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;after_success&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;: we added coveralls-lcov coverage/lcov.info&lt;/em&gt; here to send the coverage report.&lt;/p&gt;

&lt;p&gt;So that is it, you have now configured travis-ci and coveralls with flutter and should be able to see your test coverage 😄 😄 😄.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus Points&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can add a badge to the project readme to allow people to know how the project is tested. To add a coverage/coveralls badge, sign in and open the project on coveralls, click on settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZscAYBZM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Aohh4lHAkLT9FMlN5_cxsRQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZscAYBZM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Aohh4lHAkLT9FMlN5_cxsRQ.png" alt="" width="800" height="430"&gt;&lt;/a&gt;Project board coveralls.io&lt;/p&gt;

&lt;p&gt;In the repo settings, you would see an &lt;strong&gt;Embed&lt;/strong&gt; dropdown button beside a readme badge. Clicking on this button would provide you with a variety of badge option. For this &lt;a href="https://github.com/Mastersam07/PiggyX"&gt;project&lt;/a&gt;, I used the Markdown variety and added to the project README.md&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8JOKyYex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AGZkMfqNFXdNaX0ZwDLyFiA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8JOKyYex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AGZkMfqNFXdNaX0ZwDLyFiA.png" alt="" width="800" height="429"&gt;&lt;/a&gt;Repo settings coveralls.io&lt;/p&gt;

&lt;p&gt;To add travis build status badge, sign in and open the project on travis. You should see a badge with a build status on the project dashboard. The build status varies and could be unknown, failed, passed, error as the case may be. Click on this build status badge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UAUWFGUX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2APtuRVQVA7bKvTrbWEJsPGw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UAUWFGUX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2APtuRVQVA7bKvTrbWEJsPGw.png" alt="" width="800" height="429"&gt;&lt;/a&gt;project dashboard travis-ci&lt;/p&gt;

&lt;p&gt;Some sort of popup should appear on your screen providing you options for the status image. For this &lt;a href="https://github.com/Mastersam07/PiggyX"&gt;project&lt;/a&gt;, I integrated on the master branch and used the markdown status image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pr9hhxzg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A2kSEI7-e6v4Y5lyObBastg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pr9hhxzg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A2kSEI7-e6v4Y5lyObBastg.png" alt="" width="800" height="428"&gt;&lt;/a&gt;status image travis-ci&lt;/p&gt;

&lt;p&gt;And that’s it! Now after proper build on Travis, you will be able to see your test coverage and some shiny badge in your project💃 💃 💃. The full example is available &lt;a href="https://github.com/Mastersam07/PiggyX"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BpEUKSHE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/784/1%2Aj9F4RV0gJs7ZzL3uCQkD8w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BpEUKSHE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/784/1%2Aj9F4RV0gJs7ZzL3uCQkD8w.png" alt="" width="784" height="409"&gt;&lt;/a&gt;&lt;a href="https://github.com/Mastersam07/PiggyX/blob/master/README.md"&gt;&lt;/a&gt;&lt;a href="https://github.com/Mastersam07/PiggyX/blob/master/README.md"&gt;https://github.com/Mastersam07/PiggyX/blob/master/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any questions, feel free to leave a comment 🙂.&lt;/p&gt;




</description>
      <category>android</category>
      <category>flutter</category>
      <category>testing</category>
      <category>ios</category>
    </item>
    <item>
      <title>Setting Up CI/CD For Flutter With Codemagic</title>
      <dc:creator>Samuel Abada</dc:creator>
      <pubDate>Sat, 18 Jan 2020 07:29:26 +0000</pubDate>
      <link>https://dev.to/mastersam07/setting-up-ci-cd-for-flutter-with-codemagic-4nl1</link>
      <guid>https://dev.to/mastersam07/setting-up-ci-cd-for-flutter-with-codemagic-4nl1</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rCJPXFNn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AipEtTnphFltMyI1oMlRmHA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rCJPXFNn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AipEtTnphFltMyI1oMlRmHA.png" alt=""&gt;&lt;/a&gt;CI/CD For Flutter Apps&lt;/p&gt;

&lt;p&gt;Building good software and writing code quickly to achieve business goals seems like some dilemma as claimed by some programmers. Developers want to build products faster and also write good software at the same time. With CI/CD (Continuous Integration/Continuous Delivery), this is no longer an issue and yes, everyone wins 😊 😊 😊.&lt;/p&gt;

&lt;p&gt;The purpose of CI/CD is to continuously merge working product versions to the main development branch and frequent build execution to discover potential bugs and resolution of integration problems. CI improves on the stability of the product with each development cycle since each time code is pushed, automatic builds are triggered and integration tests are run to ensure that the new units being added do not “break” the previously successfully built application. If the test fails, the build fails hence ensuring that the app remains stable. When it comes to preparing an app for release, that is “Continuous Delivery” as “Continuous Deployment” is responsible for automatic deployment to production. I guess it’s clear now 😉 😉 😉.&lt;/p&gt;

&lt;p&gt;Today, there are a good number of tools for setting up CI/CD for many programming languages. I will be walking us through setting up CI/CD for a &lt;a href="https://flutter.dev/"&gt;Flutter&lt;/a&gt; application with &lt;a href="https://codemagic.io/"&gt;CodeMagic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YRx-IFdA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/940/1%2ALqr1TODb0RuqQgEP92VDlw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YRx-IFdA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/940/1%2ALqr1TODb0RuqQgEP92VDlw.jpeg" alt=""&gt;&lt;/a&gt;&lt;a href="https://blog.codemagic.io/getting-started-with-codemagic/"&gt;&lt;/a&gt;&lt;a href="https://blog.codemagic.io/getting-started-with-codemagic/"&gt;https://blog.codemagic.io/getting-started-with-codemagic/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CodeMagic is a CI/CD tool and like other CI/CD tools, it starts with a Git repository or some decentralized repository at least. When code is pushed to this repository, it triggers a new build process. If the build fails at any point, it will halt any further progress of the CI/CD process and give logs so we know where exactly the issue came from. Successful builds deliver the application artefacts for iOS and Android for installation on the respective devices. Codemagic allows to test and release Flutter apps without issues and with no configuration. As a developer, you can run custom scripts and create custom workflows for your apps. Cool right 😎😎😎? Lets Start…&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Requirements&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Setting up your Flutter app on Codemagic, you need a couple of things.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Obviously, your Flutter app with some tests.&lt;/li&gt;
&lt;li&gt;The Flutter app should be hosted on GitHub, GitLab or Bitbucket.&lt;/li&gt;
&lt;li&gt;Optional code signing details, like certificates and provisioning profiles, if you want to publish to App Store or Play Store.&lt;/li&gt;
&lt;li&gt;Optional Slack workspace for sending build reports and artefacts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, we are ready to add CI/CD to our Flutter app. However, this writeup will not cover code signing and publishing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create a Github Project
&lt;/h4&gt;

&lt;p&gt;Create your flutter application and push it to GitHub. For the purpose of this tutorial, I will be using the WhatsApp status saver built in my previous article. The codes are available on Github. Click &lt;a href="https://github.com/Mastersam07/wa_status_saver"&gt;here&lt;/a&gt; to access the codes. You can read the previous articles &lt;a href="https://dev.to/mastersam07/whatsapp-status-saver-downloader-using-flutter-4e1i-temp-slug-8481420"&gt;here&lt;/a&gt; and &lt;a href="https://dev.to/mastersam07/whatsapp-status-saver-downloader-using-flutter-ii-2jmo-temp-slug-1501806"&gt;here&lt;/a&gt; and follow the development process.&lt;/p&gt;

&lt;h4&gt;
  
  
  Register For CodeMagic
&lt;/h4&gt;

&lt;p&gt;Visit &lt;a href="https://codemagic.io/"&gt;codemagic.io&lt;/a&gt; and sign up using your GitHub, Bitbucket or GitLab account. You can connect all of these repositories with one Codemagic account, so you can have all of your apps in one place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M_t40VId--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/763/1%2AqjzqNidJzmAmUeP3bFnKvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M_t40VId--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/763/1%2AqjzqNidJzmAmUeP3bFnKvw.png" alt=""&gt;&lt;/a&gt;Sign In to Codemagic&lt;/p&gt;

&lt;h4&gt;
  
  
  Your First Build
&lt;/h4&gt;

&lt;p&gt;Once you sign in, you will see the Codemagic dashboard with the app repositories. You can filter the list or search apps to find your Flutter app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g8Hbt_10--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Aw_NNnXfRObJuMoeAkSeoCw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g8Hbt_10--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Aw_NNnXfRObJuMoeAkSeoCw.png" alt=""&gt;&lt;/a&gt;Application Overview&lt;/p&gt;

&lt;p&gt;For apps which you are configuring for the first time, you can start by clicking “Start your first build” button. In the above screenshot, we have a not applicable button as this app has been built previously.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Build Workflow
&lt;/h4&gt;

&lt;p&gt;When you start a build, Codemagic will create a workflow for your Flutter project. You can also customize it to suit your needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--05_miX6v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/796/1%2A5gCpIyBoniQGTt3H_EkdvQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--05_miX6v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/796/1%2A5gCpIyBoniQGTt3H_EkdvQ.png" alt=""&gt;&lt;/a&gt;Build Workflow&lt;/p&gt;

&lt;p&gt;From the above, we can see that the default workflow will start the build with the following steps — preparing the build machine, fetching app sources(checking out the source code), installing Flutter dependencies, running tests, building the apps and publishing the artefacts.&lt;/p&gt;

&lt;h4&gt;
  
  
  Build Report
&lt;/h4&gt;

&lt;p&gt;Below is an overview of the details when the build is finished.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fH1mfJM0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AJByeLsAGcos8WrVbxBqeJQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fH1mfJM0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AJByeLsAGcos8WrVbxBqeJQ.png" alt=""&gt;&lt;/a&gt;Build Report&lt;/p&gt;

&lt;p&gt;An email will also be sent to you with all these details. You can also set up your Slack channel to receive the build reports. The mail you receive should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2IPk39Zw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/641/1%2A_UVV1QHM8cgaCauer53OEw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2IPk39Zw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/641/1%2A_UVV1QHM8cgaCauer53OEw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, we have just added CI/CD support for our Flutter app without any configuration 😋 😋 😋. This is the magic in Code-magic 😁😁😁.&lt;/p&gt;

&lt;p&gt;But wait, what if your build fails? 😪 😪 😪. Do not worry, it will not end in tears 😉.&lt;/p&gt;

&lt;h4&gt;
  
  
  Build Failures
&lt;/h4&gt;

&lt;p&gt;Well, not all builds are successful. I have had my own fair share of failed builds 😪 😪 😪 but then we can always resolve it as Codemagic provides very detailed logs of each step regardless of whether it passes or fails 😃 😃 😃. Here we can see a failing unit test and all of the details are logged in the build failure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YGSOVkIw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AqYUawlc9ORv4dyObRjXMMw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YGSOVkIw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AqYUawlc9ORv4dyObRjXMMw.png" alt=""&gt;&lt;/a&gt;&lt;a href="https://blog.codemagic.io/"&gt;blog.codemagic.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, integration tests and widget tests are passing, but the unit test is failing. We can then fix the problem to fix the build. When tests fail, you will also get an email about the test failure with link to the build logs. I will cover integration, unit and widget tests in another article 🤓 🤓  &lt;strong&gt;🤓&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Workflow And Custom Workflow
&lt;/h3&gt;

&lt;p&gt;Default flow covers all the basics needed for the CI/CD but then, you will want to add some custom things as part of your build process. You may want to send build status to some third party communication channel such as Slack for example. Let’s dive into customizing a workflow.&lt;/p&gt;

&lt;h4&gt;
  
  
  Build Phase
&lt;/h4&gt;

&lt;p&gt;Codemagic build phase of Flutter apps has various options to define the build process. You can start by selecting which branches to build and how to trigger builds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FQwUWmaT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Ai271If95hy80sF4Lj92r3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FQwUWmaT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Ai271If95hy80sF4Lj92r3g.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also select other categories like Flutter version, build configuration, platform, Xcode version and custom build arguments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZFMYaYiW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/773/1%2AE9-L6u_P-PUQ-6ejuh1zdw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZFMYaYiW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/773/1%2AE9-L6u_P-PUQ-6ejuh1zdw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above is the default workflow settings. You can customize the build process by changing the default behaviour. Also, you can add a custom script before every phase. It's easy to setup. Just click on the + sign on top of each phase. See the below image for what you are expected to see.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4V5FQnnx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AZejfhlfaBegCdBnVV3RKQw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4V5FQnnx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AZejfhlfaBegCdBnVV3RKQw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can add any command in the pre-build script say you want to install some dependencies.&lt;/p&gt;

&lt;h4&gt;
  
  
  Test Phase
&lt;/h4&gt;

&lt;p&gt;Here, you can enable or disable execution. This phase has two sections, one for the unit and widget tests and the other is for the integration tests. The test settings are kind of straightforward. You set how the tests affect the build. You can enable Flutter analyzer, enable Flutter test which runs the unit tests in your application. You can deactivate the automatic build failures resulting from failed tests here, though this is not a recommended option for builds going out to production at least. Always save the settings for each individual section since there is no universal button to save all settings. Codemagic automatically recognizes the widget, unit and integration tests and enables them by default as you can see below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8rOFpEvU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A1o4S3gxklLt26i-HXWl8CA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8rOFpEvU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A1o4S3gxklLt26i-HXWl8CA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;we can use custom test commands by disabling the default behaviour. For example, say we want to run just UI tests from the command line, we can add a pre-test phase with the command:&lt;br&gt;&lt;br&gt;
$flutter drive --target=test_driver/main.dart&lt;/p&gt;

&lt;h4&gt;
  
  
  Publish Phase
&lt;/h4&gt;

&lt;p&gt;By default, only the email is configured if you have provided it with GitHub/Bitbucket/GitLab as we have to explicitly configure any other aspects of the Publishing phase, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uploading the code signing assets for Android and iOS&lt;/li&gt;
&lt;li&gt;Setting up slack&lt;/li&gt;
&lt;li&gt;Providing Google Play and App Store Connect details for publishing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--adqmjRa5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/993/1%2AS75B9vVsQGw87xLz4Dv86Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--adqmjRa5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/993/1%2AS75B9vVsQGw87xLz4Dv86Q.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even with these, you can also write some custom scripts to deploy the artefacts to third-party services. With the power of scripting, we can configure custom workflows in Codemagic to define a stronger build process for Flutter apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple workflows
&lt;/h3&gt;

&lt;p&gt;Codemagic allows us to create multiple workflows to meet different requirements of various configurations and software versions. Create a new workflow by duplicating the existing one and renaming it. To do this, follow these easy steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to app settings.&lt;/li&gt;
&lt;li&gt;In the Workflow settings section, click Duplicate workflow. Select the workflow and rename it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Build Status Badge
&lt;/h4&gt;

&lt;p&gt;You want to include a build status badge to your GitHub repo so the status of your build is shown? Say no more. Click on the settings icon in your Codemagic build. A screen as shown below should appear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f4-ZpWZP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AIevNn0gL5lDqbliMmwyaZw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f4-ZpWZP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AIevNn0gL5lDqbliMmwyaZw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the right-hand side, we can see a build badge. For builds that passed, colour is green and for failed builds, the colour is red. You could choose to make the builds available publicly. You can also add the badge to your repo by either a markdown or a badge URL which you could add to your README.md. Click on the arrow/dropdown button beside the badge and you should see the badge URL and markdown. Copy it and use where necessary. We should have a shiny badge as shown below in the Github repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i1Td8-4b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A73QX_K3Ksm1T1xiDcoEelA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i1Td8-4b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A73QX_K3Ksm1T1xiDcoEelA.png" alt=""&gt;&lt;/a&gt;&lt;a href="https://github.com/Mastersam07/wa_status_saver"&gt;&lt;/a&gt;&lt;a href="https://github.com/Mastersam07/wa_status_saver"&gt;https://github.com/Mastersam07/wa_status_saver&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And just like that, your Workflow is set and all the triggers are in place 😍 😍 😍. When you push code, make a pull request, it will trigger the magic and boom your app will be delivered and deployed to all interested parties through the preset channels.&lt;/p&gt;

&lt;p&gt;Head to &lt;a href="https://codemagic.io/"&gt;codemagic.io&lt;/a&gt; and try it out. The user experience is 💥 💥 💥.&lt;/p&gt;

&lt;p&gt;You can also visit Codemagic blog — &lt;a href="https://blog.codemagic.io/getting-started-with-codemagic/"&gt;https://blog.codemagic.io/getting-started-with-codemagic/&lt;/a&gt;&lt;/p&gt;




</description>
      <category>testing</category>
      <category>android</category>
      <category>ci</category>
      <category>flutter</category>
    </item>
  </channel>
</rss>
