Being a Remarkable C# .NET AutoCAD Developer Scott McFarlane
Join the conversation #AU2015
Class summary If you have a passion for developing Microsoft .NET applications for AutoCAD software, this class will help propel your skills to the next level. We will explore how you can apply the fundamental principles of good software design in the context of AutoCAD software. We will examine typical problems found when using the AutoCAD API and identify common patterns that you can simplify to eliminate duplicate code. You will learn how to capitalize on advance features of the C# language to maximize code reuse. You will also learn how to use abstraction to decouple your code from AutoCAD software to improve maintainability, testability, and overall code quality.
Key learning objectives At the end of this class, you will be able to:
Learn how to use advanced C# features to reduce duplicate code Discover common patterns when using the AutoCAD API Learn how to apply abstraction to increase testability Learn how to apply additional software design principles to improve code quality
Our Goal? Eliminate Duplication.
Don't Repeat Yourself (The DRY Principle) Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
Helper Methods…
Delegates…
What is a Delegate?
A pointer to a function, which can be… Assigned to variables Passed as arguments Invoked through a variable reference
Generic Delegate Classes
Action – no parameters, no return value. Action – one parameter, no return value. Action – two parameters, no return value. Action – three parameters, no return value. Etc…
Func – no parameters, return value of the specified type. Func – one parameter, return value of the specified type. Func – two parameters, return value of the specified type. Func – three parameters, return value of the specified type. Etc…
Extension Methods…
Generics…
Using LINQ with the AutoCAD API
Using LINQ with the AutoCAD API
A very basic LINQ example
Could also be written as
How Do the Following Declarations Differ?
IEnumerable and IEnumerable
IEnumerable is defined as follows:
IEnumerable extends IEnumerable as follows:
IEnumerator and IEnumerator
IEnumerator is defined as follows:
IEnumerator extends IEnumerator as follow`s:
Some AutoCAD Classes that Implement IEnumerable
SymbolTable (base class for all symbol tables) AttributeCollection BlockTableRecord ObjectIdCollection SelectionSet DBDictionary
The Current property of the IEnumerator returns an ObjectId.
Automated Testing
Why Am I Not Doing Automated Testing?
My programs always work correctly I never need to modify or refactor my code I enjoy spending hours tracking down bugs I haven’t yet realized the benefits I haven’t learned to do it well My code is difficult to test I don’t have the time/budget
Code-Driven Testing Frameworks
The growing family of code-driven testing frameworks have come to be known collectively as xUnit Based on a design by Kent Beck, originally implemented for Smalltalk as SUnit, then ported to Java as JUnit. Wikipedia lists over 360 testing frameworks for over 70 different languages and environments.
Basic xUnit Architecture
Test Case Also called Testcase Class or Test Class Implemented as an interface, abstract base class, or class attribute Contains one or more Test Methods Typically shares the same Test Fixture
Test Method Contains the logic for each test
Basic xUnit Architecture
Test Suite A set of tests that share the same Test Fixture
SUT (System Under Test) The actual class/method we are testing
Test Fixture All the things we need to have in place to run a test
4-Step Test Execution
1. Setup 2. Exercise 3. Verify 4. Teardown
Tests Should Help Us Improve Quality What is Quality? 1. Is the software built correctly? 2. Have we built the correct software?
Tests act as an executable specification Tests help prevent bugs Tests help localize bugs But… Testing does not equal quality “Trying to improve the quality of software by doing more testing is like trying to lose weight by weighing yourself more often.” - Steve McConnell
Tests Should Help Us Understand the Code
Rather than pouring over the code to answer: What is this code supposed to do? What would the result be if . . . ?
Tests should show the test reader how the code is supposed to work. Tests are less likely to become out-of-sync with code than documentation
Tests Should Reduce (Not Introduce) Risk Tests provide a safety net for incremental development and refactoring Without tests, we are reluctant to make changes or try different approaches – risk is too high.
Writing good tests is not easy – It takes lots of practice Keep tests simple, clean, easy to read Don’t be afraid to use Test Utility Methods.
Dangers (Risks) of Automated Testing Putting test logic in production code Testing too many things at once Testing too little
Tests Should Be Easy to Run
Tests must be…
Fully automated Self-checking Independent Repeatable Fast
Tests Should Require Minimal Maintenance as the System Evolves Around Them
Test only public interfaces and outcomes when possible Avoid testing private methods directly (implementation details) Use Mocks only when necessary Minimize duplicate code in tests Tests should facilitate (not hinder) our ability to make changes to the production code.
Summary of Goals
Trustworthiness Are you able to run your test and trust the results?
Maintainability Treat your test code with the same care as your production code.
Readability Can you look at a test and quickly understand what is being tested and why it failed?
Definitions (Meszaros) Test Double – Any object that is used in place of a real object for the purposes of testing. Mocks, stubs, fakes and dummies are all test doubles. Mock – an object that can monitor the indirect outputs (behavior) of the SUT. Your tests assert against the mock object (rather than the SUT itself) to see if it has passed. Stub – an object that can control indirect inputs into the SUT. A test can use multiple subs, but can only have one mock object. Fake – an object used by the SUT that behaves exactly like the real object, but may be used to isolate the SUT from external resources. An in-memory database is an example of a Test Double. Dummy – an object that is required by the SUT to run, but plays no active role in the test.
4-Step Test Execution
1. Setup 2. Exercise 3. Verify 1. 2. 3.
Return Value/Exception State Verification Behavior Verification
4. Teardown
Behavior Verification vs. State Verification Behavior Verification
Class under test
State Verification
Test
Test
Communicate
Communicate
Mock Dependent Component
Class under test
Stub Dependent Component
Unit Tests vs. Integration Tests Unit Tests Should run w/o config Isolated No teardown required Run in memory Fast Repeatable Run often
Integration Tests May require config Use external resources May require teardown May use disk or database Slow May not be repeatable Run less often
Do not mix - Keep in separate projects/assemblies
Unit Testing “Thorns”
MessageBox.Show() Timer WebClient System.IO (File, etc.) DateTime.Now Singletons/Statics/Globals (anything that could be shared among tests)
Testing Tools * Used in this class Testing Framework (xUnit) MSTest * NUnit MbUnit/Gallio
Isolation Framework NSubstitute * Moq FakeItEasy
Other
Fluent Assertions * NCrunch * ($)
Obstacles to Testing AutoCAD Code
Our programs are hosted by AutoCAD Heavy user interaction Code is tightly coupled with AutoCAD API
Example
Abstraction and Dependency Injection
Why is Abstraction Important?
The Problem Classes often contain dependencies on other classes to do their work. If a class makes assumptions about how its dependent classes are implemented, the class becomes difficult to reuse in combination with other components. Such classes are also very difficult to unit test because they cannot be isolated from their dependencies.
Why is Abstraction Important?
The Solution Establish a common set of protocols by which classes interact, separately from the classes themselves. Promotes the use of software design patterns (particularly dependency injection) that result in code that is more testable, extensible, maintainable, scalable, and reusable.
Typical Application Component
User Interface
Exception Handler Business Logic
Logger FILE
Component
Data Access
Configuration Web Service Client
Typical Application IHostApplication
Host Application
Component IMessageBox
Interface
User Interface
IProgressBar
IExceptionHandler
Exception Handler
Business Logic
Logger ILogger
FILE
Component Interface
Data Access IDataRepository
IConfiguration
Configuration
ISomeWebService
Web Service Client
Typical Application Test Double IHostApplication Test Double IMessageBox
Interface
Test Double
IProgressBar
Test Double IExceptionHandler Business Logic
Test Double ILogger
Test Double Interface
Test Double IDataRepository
IConfiguration
Test Double Test Double ISomeWebService
Example 1 public class Example1 { public void DoTheWork() { DataRepository dataRepository = new DataRepository(); Logger logger = new Logger(); logger.Log("Getting the data"); DataSet theData = dataRepository.GetSomeData(); // Do some work with the data... logger.Log("Done."); } }
Example 2 public class Example2 { private readonly IDataRepository _dataRepository; private readonly ILogger _logger; public Example2(IDataRepository dataRepository, ILogger logger) { _dataRepository = dataRepository; _logger = logger; } public void DoTheWork() { _logger.Log("Getting the data");
DataSet theData = _dataRepository.GetSomeData(); // Do some work with the data... _logger.Log("Done."); } }
Software Engineering Principles Separation of Concerns – This class is now only responsible for the specific job it was designed to do. Abstraction – By using interfaces, we have established a set of protocols by which the components interact, separately from the classes themselves. Inversion of Control – The class has relinquished control of the creation and initialization of its dependencies. Dependency Injection – This pattern is based on Inversion of Control, and describes the way in which an object obtains references to its dependencies.
Application Architecture User Interface AcCoreConsole AutoCAD I/O
AutoCAD
Main Plug-in Code (Bootstrapper)
AcMgd.dll
Concrete Dependencies Logging Configuration Databases Web Services Etc…
Implements
AutoCAD Specific Logic
Business Logic
Unit Tests
AcDbMgd.dll AcCoreMgd.dll
Shared Abstractions Interfaces Domain Objects
Depends on AutoCAD API No dependency on AutoCAD API
Example
Autodesk is a registered trademark of Autodesk, Inc., and/or its subsidiaries and/or affiliates in the USA and/or other countries. All other brand names, product names, or trademarks belong to their respective holders. Autodesk reserves the right to alter product and services offerings, and specifications and pricing at any time without notice, and is not responsible for typographical or graphical errors that may appear in this document. © 2015 Autodesk, Inc. All rights reserved.