Learning Objectives ?· Web viewAutomated testing, which includes unit testing and integration testing,…

  • Published on
    08-Jun-2018

  • View
    212

  • Download
    0

Embed Size (px)

Transcript

<p>Insert Class Title as per Title Page</p> <p>Automated Testing with the AutoCAD .NET API</p> <p>Automated Testing with the AutoCAD .NET API</p> <p>Scott McFarlane Woolpert, Inc.</p> <p>CP2654Automated testing, which includes unit testing and integration testing, is one of the cornerstones of the Agile development process. Yet we see it as difficult and expensive, especially for programs written with the AutoCAD .NET API. So what makes it so difficult? What are the obstacles? Can we overcome them? This class will explore the topic of automated testing, specifically for .NET programs written for products based on AutoCAD software. You will learn how good programming practices, such as single responsibility, abstraction, interface development, and dependency inversion, go hand in hand with writing testable code, and how to use these techniques to decouple your code from the AutoCAD API, enabling you to unit test your business logic in Microsoft Visual Studio. Well also look at a few of the popular testing tools, including the Gallio Automation Platform, which integrates AutoCAD into its test runner.</p> <p>Learning Objectives</p> <p>At the end of this class, you will be able to:</p> <p> Recognize the obstacles to doing software testing, particularly with programs written for AutoCAD</p> <p> Understand the goals, philosophy and principles of test automation</p> <p> Appreciate the benefits of test automation</p> <p> Use the correct Terminology: What is a mock, a stub, a fake, a dummy?</p> <p> Use abstraction and dependency inversion to write testable code</p> <p>About the Speaker</p> <p>Scott is senior software engineer for the Infrastructure service line at Woolpert, Inc. He specializes in custom database applications that use Autodesk software in the AEC, FM and GIS industries. He has more than 30 years of programming experience, and has been developing for Autodesk platforms since 1986. He is the author of AutoCAD Database Connectivity from Autodesk Press, as well as several articles. Scott has attended every AU, and has been a speaker since 1996. He also served two two-year terms on the AUGI Board of Directors.</p> <p>scott.mcfarlane@woolpert.com</p> <p>Introduction</p> <p>My name is Scott McFarlane. If youre like me, your formal training or college degree was focused on some design-related discipline (I am a degreed architect). You probably also had a strong interest in computer programming, but no formal training. Sure, I took my share of computer programming courses in college with very useful programming languages of the day, such as Fortran, COBOL, and Assembler. But generally I consider myself self-taught when it comes to software development. I started using LISP within the first month I ever started using AutoCAD. From there I jumped on each AutoCAD API as it was added. Today, Im pretty much exclusively a C#.NET developer when it comes to AutoCAD.</p> <p>In recent years, I have become a student of software engineering, software design patterns, and best practices. These practices are the kinds of things Ive been doing (in some form or another) for many years, yet these days they are formalized, studied and documented. Let me stress that I will forever be a student of these practices continuously learning and adapting my software design style as my knowledge grows. In this class, while I try to offer a better way to solve certain problems, there is likely an even better way that I havent explored. I always welcome discussion and debate on these subjects so please feel free to ask questions during the class, track me down at the conference, or contact me after the conference is over.</p> <p>Obstacles to Testing AutoCAD Code</p> <p>Automated testing is an important and integral part of the software development process. However, as AutoCAD programmers, we face unique challenges that often discourage or prevent us from writing tests for our programs.</p> <p>Some of these obstacles include:</p> <p>Our programs are hosted by acad.exe An AutoCAD .NET program is compiled into a .DLL, which is loaded into AutoCADs memory space. In our code, we rely on a set of assemblies specifically designed to work with AutoCAD. Once these assemblies are referenced into our code, our program will only work inside a running session of AutoCAD. This makes it impossible to use unit-testing tools inside of Visual Studio.</p> <p>AutoCAD programs typically rely on heavy user-interaction While tools exist to assist in automated user interface testing, this type of testing is very difficult, particularly with a complex program like AutoCAD. While UI testing itself is beyond the scope of this class, we will explore ways to separate the UI-specific code from the business logic code, in order to minimize the amount of UI testing that is necessary.</p> <p>But even beyond these obstacles, many developers dont write automated tests, primarily because their code is not designed to be testable. Programs written for AutoCAD are often single, monolithic applications that are tightly coupled with the AutoCAD API.</p> <p>In order to be successful with automated testing, your code must first be written in such a way that it is testable. In order to write testable code, several good programming practices must be followed, including, Single Responsibility Principle (SRP), separation of concerns, and Dependency Injection. Testing goes hand-in-hand with these practices. Use of these practices results in testable code, and good testing practices drives good design.</p> <p>Test-Driven Development</p> <p>One practice related to testing that you hear a lot about is test-driven development, or TDD. TDD is one of the cornerstones of the Agile software development methodology. It is an iterative practice that consists of first writing a (failing) test, then writing just enough code to make the test pass. The refine or write more tests, then add more code until the tests pass. When TDD is strictly followed, you never write a single line of production code that doesnt already have a test written for it.</p> <p>For those of us who have been writing production code for years with little or no automated testing, making a jump into a strict TDD process is extremely difficult. But even if we dont practice TDD, automated testing should not be left to the end of the project. Start writing tests as early as possible, either before you write the code, or at least concurrently with the code. Having tests written early will yield significant benefits, including:</p> <p>Testing early forces you to write testable code. Rather than writing a bunch of production code and worrying about whether it will be testable, writing your tests first forces your code to be testable without having to think about it.</p> <p>Tests provide a safety net for refactoring. Refactoring is the process of modifying code to improve design, make the code more readable and reduce duplication, without changing its overall functionality. Tests give you constant feedback during the refactoring process, so you can feel confident that changes made during refactoring havent broken anything.</p> <p>Types of Testing</p> <p>The key to successful testing is to design the system using a loosely coupled, component-based architecture. This allows you to test each component in isolation, by substituting fake versions of other dependent components. It also allows you to easily tier the testing process into the following levels:</p> <p>Unit Tests These tests verify the behavior of individual classes and methods in isolation from other components. Unit tests are typically written concurrently with the production code being tested, using tools available within the specific development environment (such as Visual Studio). These must be executed continuously as the code evolves to ensure that new functionality does not negatively affect the behavior of existing functionality. And, as mentioned earlier, unit tests also provide a safety net for code refactoring or code modifications due to evolving requirements.</p> <p>Component Tests These tests verify the behavior of specific groups of classes or components and how they interact with one another. Component tests can take the form of either automated tests within the development environment, or as separate executable scripts that are run outside the development environment.</p> <p>Integration Tests These tests verify the system as a whole, within its production environment, or at least within a development environment that duplicates the production environment as closely as possible. Integration tests will usually be separate executable programs so that they can be deployed and run in the production environment.</p> <p>Acceptance Tests These tests are performed by the end-user, and verify the behavior of the entire system with respect to the software requirements. While some acceptance tests may be automated, there will always be a portion of the acceptance testing that requires some human interaction.</p> <p>All of the testing approaches described above must occur as early as possible, and as often as feasible, throughout the development process. This allows you to identify and address any issues with minimal impact to the overall schedule and budget of your project.</p> <p>Goals of Automated Testing</p> <p>Regardless of which type of automated test you are writing, you should keep in mind the following good practices:</p> <p> Tests should be fully automated and easy to run the easier your tests are to run, the more likely you will be to run them, and run them often. They should also run fast enough to provide timely feedback.</p> <p> Test should be reliable Tests are useless if you cant trust them. They should be repeatable, independent, and self-checking. Any data used for an automated test should be isolated by test. Shared data between tests can lead to unreliable tests.</p> <p> Tests should be easy to maintain You should treat your test code with the same care and attention as your production code. As your code evolves, your tests will evolve as well. If tests are hard to maintain, they will quickly become obsolete and you will stop using them.</p> <p> Tests should help us understand the system Tests can be a great source of documentation, and since they are compiled code they will never become out-of-sync with production code like code comments tend to do.</p> <p>The Big Split</p> <p>Over the past several releases, Autodesk has been working to separate the AutoCAD business logic from the AutoCAD user interface. This effort, which allows Autodesk to share a single codebase for its OS-independent business logic, has been referred to as The Big Split. In AutoCAD 2013 this work is complete, and we now have a three-tier architecture as shown in the diagram below.</p> <p>It is important to understand that each tier is dependent on the tiers below it, but knows nothing about the tiers above it.</p> <p>At the lowest tier you have the AcDb functionality, which is concerned with the DWG database logic. At this level, you can develop components such as ObjectDBX (object enablers) and those that work with RealDWG.</p> <p>At the middle tier you have the AcCore functionality, which is concerned with AutoCADs core business logic. At this level, you can develop components and applications that work with AcCoreConsole (a command line only version of AutoCAD), and AutoCAD WS.</p> <p>These two lower tiers together represent the AutoCAD Core Engine. This is the common platform upon which components at the top tier, i.e. AutoCAD for Windows, AutoCAD for Mac, AutoCAD WS and AutoCAD Console are developed.</p> <p>AcCoreConsole</p> <p>So by now youre probably asking, What is AcCoreConsole, and what can it do for me as a developer? Think of AcCoreConsole as a command-line only version of AutoCAD. It is a console application that has nearly all of the core functionality of AutoCAD, but without the heavy graphical user interface and it launches in less than 1 second (on my machine, anyway). It is sometimes referred to as a headless AutoCAD.</p> <p>Because of its fast loading time, and the absence of the UI overhead, AcCoreConsole is a perfect tool for running batch operations and scripts. But it also provides the perfect platform for certain types of automated testing.</p> <p>Separation of Concerns</p> <p>So why am I bringing this up? At a high level, the big split represents an effort to separate the concerns of database (DWG) access, business logic, and UI. As AutoCAD developers, we can learn a lot from this, and we should seriously consider a similar approach in our own applications.</p> <p>So how should we separate out our code? The very first thing we should do is determine what parts of our code are dependent on AutoCAD, and what parts can we separate out into non-AutoCAD-dependent components.</p> <p>Then within those components that are dependent on the AutoCAD API, you should consider further separating out the parts that depend on the core tier (acdbmgd.dll and accoremgd.dll) with those that depend on the UI tier (acmgd.dll).</p> <p>Unit Tests vs. Integration Tests</p> <p>Below is a table that shows some of the key differences between unit tests and integration tests.</p> <p>Unit Tests</p> <p>Integration Tests</p> <p>Should run without configuration</p> <p>May require configuration</p> <p>Isolated</p> <p>Use external resources</p> <p>No teardown required</p> <p>May require teardown</p> <p>Run in memory</p> <p>May use disk or database</p> <p>Run in development environment</p> <p>May require a separate environment</p> <p>Fast</p> <p>Slow</p> <p>Repeatable</p> <p>Should be repeatable, but may not be</p> <p>Run often</p> <p>Run less often</p> <p>Unit Tests and AutoCAD</p> <p>Based on the above table, a true unit test must be able to isolate the code being tested from any external sources, and operate completely in memory and preferably within the IDE (i.e. Visual Studio). By this definition, it is impossible to write unit tests for code that references any of the AutoCAD DLLs. So, to properly unit test your AutoCAD programs, you must be able to isolate that parts you want to test from the parts that truly need the AutoCAD API.</p> <p>Lets look at a very simple example.</p> <p>Examine the code below. This is a simple AutoCAD program that changes the color of all the circles in the drawing to a specific color based on the circle radius. Small (r &lt; 1.0) circles are changed to yellow, medium (1.0 10.0)</p> <p>{</p> <p>circle.ColorIndex = 1;</p> <p>}</p> <p>else</p> <p>{</p> <p>circle.ColorIndex = 3;</p> <p>}</p> <p>}</p> <p>}</p> <p>tr.Commit();</p> <p>}</p> <p>catch (Exception ex)</p> <p>{</p> <p>tr.Abort();</p> <p>document.Editor.WriteMessage(ex.ToString());</p> <p>}</p> <p>}</p> <p>}</p> <p>The majority of this code is boiler plate code that you see in any program that manipulates AutoCAD entities in model space. There is really no need to test that code. The part of the code we really want to test is the logic that sets the circle color based on the radius.</p> <p>So the first step is to extract that code into its own method:</p> <p>private void SetCircleColor(Circle circle)</p> <p>{</p> <p>if (circle.Radius &lt; 1.0)</p> <p>{</p> <p>circle.ColorIndex = 2;</p> <p>}</p> <p>else if (circle.Radius &gt; 10.0)</p> <p>{</p> <p>circle.ColorIndex = 1;</p> <p>}</p> <p>else</p> <p>{</p> <p>circle.ColorIndex = 3;</p> <p>}</p> <p>}</p> <p>By d...</p>