Monthly Archives: August 2010

Consistency: Your best friend or your worst nightmare

Consistency: We developers love it don’t we?

We come up with beautiful architectures making sure everything is consistent. If we use a service in one place, we’ll use it everywhere. If we have a ViewModel for showing data, we’ll use a ViewModel everywhere, even if it’s not needed. Because we need to be consistent.

Partly I guess this is motivated because we think that being consistent helps ourselves and teammates understand what’s going on, even if that drives us to YAGNI and a series of WTF’s of why we did something that’s not required. We do it to be consistent!

It might seem inoffensive, but consistency can be damaging. It leads us into trying to homogenize everything despite it not being the appropriate approach in many cases.

So next time someone asks:

image9ba8

…it’s cause we like to be consistent!

Breaking consistency when required not only lowers levels of WTF’s, it can even help scalability!

Regression tests, do it now.

When you encounter a bug, do you first write a failing test before fixing it? You should. But the vast majority of us sometimes do not. External pressure (read Customer, Teammates or Management) move us in the opposite direction. Everything we’ve learnt about unit testing and good software practices go out the window, and the sheer pressure forces us to fix the issue as fast as possible! We focus more on getting the job done* and deploying than worrying about adding another test to our suite.

Adding that test is not about increasing our code coverage or patting ourselves on the back, with an optional tweet, that we are good developers. It’s about putting regression tests in place.

Of course, we could always just apply the fix, and then later, calmly add a test. However, there’s always another urgent bug fix, feature, or customer breathing down our necks, so things just get putt off. 

It’s hard to not be tempted to just fix something then and there. Fight the temptation though if you can. Write that failing test first!

[*Implicitly changing the definition of Done on the go]

Templates Galore: File Templates

Continuing with Templates, in this post we’re going to take a look at File Templates. Whereas Live Templates allow us to generate interactive code and insert this at the current cursor position, File Templates allow us to do the same but directing the output to a file. Additionally, as we’ll see, they are very useful for creating both standard and custom files efficiently.

If we open up the Templates Explorer via ReSharper | Live Templates, we can see three tabs: Live Templates, Surround Templates and File Templates.

imagede55

Similar to Live Templates, Files are organized into different categories, corresponding to the type and language of a project. By default the predefined ones include templates for Web Applications (ASP.NET) and generic applications (Console, WinForm, Libraries, etc.). There are also User defined ones as well as Shared Solution and Personal Solution, again similar to Live Templates.

On the right-hand column we have a repeated set of templates. What these allow us to do is customize the usage of templates. However, before seeing that and delving into creating new templates, let’s see how we can use the existing ones.

Using Templates in Web Applications

1. Create a new Web Application (can be MVC or WebForms) to work with.

2. There are multiple ways to create new file templates with ReSharper (right-click Solution Explorer |Add | New From Template or via the ReSharper | Edit | New From Template menu). However, by far one of the easiest and most efficient ways is what we introduced in ReSharper 5: Create New File. You can invoke this from anywhere, be it the Solution Explorer or from a file in the editor, by pressing Ctrl+Alt+Ins

image7cc0  
[Pressing Ctrl+Alt+Ins in ASPX editor]

 

imagef31f  
[Pressing Ctrl+Alt+Ins in Solution Explorer]

Since this is a Web Application, the list of Templates to choose from include all those that appeared under ASP.NET Projects in the Template Explorer. In addition, since creating a Class, Interface, Struct or Enum are still valid in the context, it will display those too.

3. Select a new file, for instance Web form with master page and provide it with a name and hit OK (Enter key)

image75dd

4. Based on the type of file, and in particular the template it contains, we will be prompted for additional information. In this case since we requested a Web Form with a MasterPage, ReSharper prompts for the MasterPage file

image50f9

5. Select the corresponding master page and hit Enter.

We’re done. With very little effort, we’ve created a new Web Page and referenced the Master Page. Comparing that to the default way of creating Web Forms (or in fact any other item) in Visual Studio, we can quickly see how much more efficient it can be. However, the true power of this lies in it’s customization, not only in the template but also in what templates we can choose from.

Customizing the Files we care about

When invoking the Create New File, the list that appears is customizable, both in regard to the contents as well as the ordering. Imagine for instance that we usually work with MVC applications and Spark View Engine and are not really interested in WebForms. Having things like Web Control or Web Form with master page pop-up only add noise to the options. Fortunately we can eliminate these easily:

1. Open up the Templates Explorer editor via ReSharper | Live Templates and select File Templates

2. Select the element we don’t want to appear when creating a new File Template. In this case we want to eliminate Web Controls and Web form xxxx

image99f6

3. Click on the red cross (X)

4. Close the Templates Explorer

5. Invoke the Create New File menu by pressing Ctrl+Alt+Ins. It should now only display the remaining entries.

image2850

Apart from eliminating entries, we can also change the sort order by dragging the entries in the Templates Explorer (right-hand column, same place we selected to delete in step 2) up and down.

Creating new File Templates

Being able to generate templates fast, customize them and edit them wouldn’t be sufficient if we couldn’t also create new ones. Fortunately, we can. Let’s see how we can easily create simple DTO objects that use for instance the DataContract and DataMember attributes.

1. Create a new project. Any type will do.

2. Open Templates Explorer via ReSharper | Live Templates and click on File Templates

3. Select the User Templates node and right-click to create New Template

a

3. In the Template Editor, type DTO for the Description and NewDTO.cs for the file name

4. Enter the following in the Editor

b

We now need to define some macros on the placeholders (variables $nameSpace$, $className$, etc.).

Note: The first [DataMember] property we’ve added is of course optional and more for demonstration purposes.

5. Select the nameSpace placeholder in the right-hand column and for the value choose “Default namespace for current file

6. Select the className placeholder in the right-hand column and for the value choose “Current file name without extension

7. Uncheck the Editable Occurrence on previous two entries. This prevents ReSharper prompting us for values

8. Save changes and close the editor

Using the new File Template

We saw that File Templates can be invoked using Ctrl+Alt+Ins (Create New File). However if we do this now, our template will not appear. That is because we need to tell ReSharper we want it on this quick list. We can do this in two ways:

Method 1: Invoke the template using an alternative option, for instance ReSharper | Edit | New From Template and selecting More..

c

This will bring up a dialog box with other templates:

d

where we can select our Template. At this point we can tick the CheckBox “Add to quicklist”, selecting the Category from the Dropdown below it.

Method 2: Alternatively, we can drag and drop the template from the Template Explorer to the Category we are interested in (for instance C#)

e

Either way we choose, from this point on, our template will now be accessible via Ctrl+Alt+Ins:

f

Summary

With File Templates we can easily create files that are used frequently, such as DTO’s, Unit Tests, Controllers or other common classes we create. In addition to providing an efficient way to create these items, we can also interact with them, just like we do with Live Templates to adapt them to our needs on each creation.

Highlighting Custom Patterns with ReSharper

A new feature that has shipped with ReSharper 5 is the Structural Search and Replace. It is a way for us to locate certain patterns in our code, and optionally replace them. However, it is more than just a Find and Replace. It allows us to somehow extend ReSharper’s inspection settings by offering us means to identify patterns and highlight them.

As many of you know, ReSharper offers a series of Suggestions, Hints, Warnings and Errors when it detects certain code patterns. For instance, when explicitly declaring a variable, ReSharper suggests you use var (and with good reason)

a

These are configurable, in that you can tell ReSharper whether you want them to be displayed as hints, suggestions, warnings or errors, which is done via ReSharper | Options | Inspection Severity

b

Setting it to Show as error for instance will display a red (or whatever color you have configured) underlining and the right-hand side border will display the a Red box indicating an error in the file.

c

If we now combine this with Solution Wide-Analysis, ReSharper will also indicate an error in the solution.

d

 

Extending Inspections

Have you ever encountered the following?

e

This is a perfect candidate for using String.Format. How about this?

f

Did you know StringBuilder has an AppendFormat? You might know that, I might, but maybe other developers don’t. It would be good to provide developers with a hint or suggestion that they could use AppendFormat. Up to version 5 of ReSharper, the inspections were limited to what ReSharper provided out of the box and the only thing we could do to extend this functionality was create/use a plug-in We can now use Structural Search and Replace to accomplish this. Let’s see how it works.

1. Open up a Solution (I’m using BlogEngine.NET but feel free to create your own with some sb.Append calls).

2. Select ReSharper | Tools | Pattern Catalog.

g

By default, the Pattern Catalog is empty.

3. Click on the Add Pattern

h

4. In this first sample, all we want to do is highlight code, so click on the Find button at the top to switch the dialog to Find mode only.

i

The Search Pattern is what we’ll use to define what we are looking for. The Description is a text which helps us identify the pattern easily in the Catalog. The box on the right is for place holders which we’ll see in a moment.

5. Enter the following pattern in the Search Pattern editor

j

What this does is indicate the pattern we are looking for. The values surrounded with $’s (same notation as that used in Live Templates) represent placeholders. A placeholder can be an expression, statement, type, etc. If it is red, it means it has not yet been defined (next step).

6. Click on the Add Placeholder button on the right box and select Expression, and enter the following values:

k

We are indicating that the placeholder sb (first one used) is an expression of type System.Text.StringBuilder

7. Click on the Add Placeholder button to define the second placeholder, args. Select Argument and enter the following values:

l

We can optionally indicate whether we want to limit the number of minimum or maximum arguments.

If both placeholders have been defined correctly, the Search pattern box should now look like this:

m

(we’ve entered a description at the bottom)

8. We need to define the type of severity we want. In this case we want it to be marked as a Suggestion. We do that using the Pattern severity dropdown and then click Save

Our Pattern Catalog should now be updated to reflect this new pattern

n

9. At this stage we can either close the Patterns dialog or Search for a pattern. Let’s see what happens if we click Search now button.

o

A Find Results window is displayed highlighting everywhere where ReSharper can find a pattern match. But what about the Pattern severity? Where does that come into play? Let’s double-click on one of the entries:

p

See the highlighting of the sb.Append call? Set the Pattern Severity to Error and that will display in Red.

q

Turn on Solution-Wide Analysis and that will list as an error!

So as we can see, the Structural Search (we haven’t replaced yet), allows us to monitor for certain code patterns in our projects. Potentially we could use it to detect certain code smells.

Fixing things up

Now that we have certain patterns located, and even highlighted, how do we go about fixing things? As we saw in the previous screenshots, we have a little icon pop up when placing the cursor on the line:

r

This context action however doesn’t offer us anything in order to fix the situation, i.e. there is no QuickFix. The reason is obvious: ReSharper doesn’t know what a fix is. Let’s show it!

1. Open up the Patterns Catalog and Edit the previously created Pattern

2. Click on the Replace button to open up the Replace editor at the bottom

s

3. Enter the following text in the Replace pattern section

t

4. Provide a description (this is what the hint of the QuickFix will dispaly)

u

5. Click on the Save button

Locate the previous sb.Append call and place the cursor on top of the line. We can now see a QuickFix icon appear (A red bulb in this case since it’s an Error)

v

Alt+Enter and we’re done!

w

 

Global Fix

What we’ve just done is a manual local fix, that is, locate the offending entry and hit Alt+Enter to apply a QuickFix. We can do this at a global scope by using the Pattern Catalog tool window.

1. Undo the prefix fix (so as to have several instances)

2. Open up the Patterns Catalog

3. Select the recently created Pattern and click on Search now. This time, instead of the Find Results dialog, we get a Replace dialog which displays all matching patterns and a Replace button

x

4. We can select the entries we want replacing (by default all checked). Click Replace

We’re done! ReSharper will now replace all occurrences. So we’ve applied a QuickFix globally.

 

There’s more

What we’ve seen here is just a simple example of how to improve on a call to a class. We can of course do more complex searches, allowing us to identify code smells, etc. The pattern we created in this tutorial is actually included in a Patterns catalog you can download from the documentation section of our web site (or click here for direct link). All you need to do is unzip it and import the XML from the Patterns Catalog window (Import button). Currently, Pattern Catalogs are PER solution.

There’s much more planned for Structural Search and Replace in vNext and we are always happy to receive feedback, so please try it out, and let us know what you think.

Losing templates on ReSharper upgrade

Derick Bailey pointed out today on Twitter that during an upgrade, he lost his ReSharper templates.

ReSharper should import the existing templates during an upgrade. However, for some odd reason, sometimes this does not occur. We managed to sort out Derick’s issue relatively quickly, but in case others might run into the same problem, here’s how to solve it:

Scenario: Upgrading from 5.0 to 5.1 in VS2010 and settings lost.

1. Close down Visual Studio 2010.

2. Go to the %AppData%\JetBrains\ReSharper folder. In there you should find subfolders for each version of ReSharper. Select v5.0.

Inside each folder there are subfolders corresponding to the different versions of Visual Studio you have installed on your machine. Select vs10.0 (corresponds to VS 2010).

3. Select the files UserSettings.xml and Workspace.xml.

4. Copy the selected files to %AppData%\JetBrains\ReSharper\v5.1\vs10.0

5. Re-start Visual Studio

 

If all goes well, you should now have your settings correctly imported. If you have run into this problem, please feel free to contact me. We need information on setups to try and see what the common denominator is for when this fails (also please store the contents of these folders since it might help us figure out what’s wrong).

Templates Galore: Live Templates

A couple of months ago I was doing a talk at DevSum, Stockholm. It was a room full of developers, 90% of whom had heard of ReSharper and 80% of them used it. What surprised me however was that only a couple of folks knew about Live Templates, which left me wondering how many outside of the that group actually knew about them too. Thus, in an attempt to expand the reach, we’ll take a look at what Live Templates and in future posts look at Surround and File Templates

Snippets on Speed

Live Templates enhances the Visual Studio Code Snippets by providing more functionality and easier management. To open up the Live Templates manager, we click on the ReSharper menu and select Live Templates:

1

ReSharper has four groups of templates:

  • Predefined: Predefined by ReSharper
  • User Templates: Templates defined by the user
  • Personal Solution Templates: Templates that are specific to a solution
  • Shared Solution Templates: Templates that are specific to a solution and can be shared with others

Each group consists of various categories based on the type of project/files we are working on. Many of these also import the default Snippets that are available in Visual Studio. From this screen there are various operations we can perform such as adding, removing, editing templates, as well as importing and them. Editing and Creating Templates is where some of the juice is, so let’s see how we can create a new template.

Creating Templates

Templates consists of literal strings and variables. The idea is to combine these to maximize efficiency when typing. They can be used for a variety of things, from creating classes, DTO’s or skeletons for unit tests, among others. Let’s say we like to create entities that usually have an Id field prefixed (this is not personal!) with the name of the class, along with some other properties. The type of my Id is not necessarily known beforehand. It can be of type string, GUID, etc. Here’s a sample:

public class Customer
{
public string CustomerId { get; private set; }
public string Name { get; private set; }
public string Email { get; private set; }
}

 

 

Let’s see the step-by-step process to create a template for this:

1. Open up the Live Template Editor and select User Templates

2. Click New Template to add a new template

2

3. This opens up the editor where we can type our text (literal strings and variables).

3

4. We need to provide a shortcut for our template, which will be used to invoke it (we’ll see how later). Type “be” in the Shortcut field. In the Description type “Business Entity”

5. Type the following in the text area (black in the figure above)

4

The different colors have meanings (these Colors can be configured under Tools | Options | Fonts and Color. Look for “ReSharper Live xxxxx” entries). There are 3 types of elements in the text:

  • text literal
  • template keyword
  • variables

The text literal is everything that is in white text, such as “public class” and “{ get; private set;}”. Anything literal will appear as is when we invoke the template. The second type of element is the $END$ keyword. This is used to tell ReSharper where we want the cursor to be positioned once we have finished invoking and interacting with the template. Finally we have the variables. These are elements that can be used to interact with the template. A variable is designated using the $ prefix and suffix. That is, anytime we type a word prefixed and suffixed with a $ sign, ReSharper will automatically convert it to a variable. The effect that this has is that when we invoke the template, we can iterate through the different variables to type a value. In our case, since we want to establish the classname during its invocation, we make the $className$ a variable. The same occurs with $idType$, since we don’t know what type we want the Id to be (this is for demonstration purposes, normally most entities have the same type for their Id). Finally comes the Id property. We said we want this to resemble the classname suffixed with Id. As such we just suffix it with the word Id.

6. Close the Editor.

7. Open up an existing file and locate an empty are where we can create a new class. Now type “b” and we should get Intellisense for all templates that start with “b”. Locate “be” and hit TAB (or ENTER)

5

ending up with:

6

ReSharper has generated the core code of the template and is now ready for our input. The cursor is currently positioned at className, where can type in the class name. Notice that as we type, the value of the property name is automatically updated, as expected. Once we finish typing className we hit TAB (or ENTER) and ReSharper positions us at the IdType entry where we can type in the type of the property (with the corresponding IntelliseSense).

8. Once the process is complete, we should end up with the following class (for the value “Customer”):

7

Classifying the Template

Currently the newly created template has been classified under “No Languages”. The main issue with this is that this template will be available pretty much everywhere, even places where it wouldn’t make sense to create a new class.

8

Let’s proceed in limiting it in scope and defining a category:

1. Select ReSharper | Live Templates and Click on Edit on the newly created template

2. Select the link at the top of the editor next to the “Available” text

9

The Template Availability dialog box will appear

10

3. Select the Language-Specific option and choose C# from the dropdown list. The dialog box should now change to show language specific options:

11

4. We can now choose the minimum version of the compiler for which this template is available (for example if it contains lambda expressions, we cannot use it versions below 3) and also choose where we want the declaration to be available. In our case we can choose Everywhere.

5. Last thing left to do is define a New Category for this template. We can do this by selecting it in the Live Templates tree, right-clicking and selecting New Category. In our case we would type C#

With this change we restrict where this template will be available and have a better classification when viewing templates.

Extending Functionality with Macros

We saw that when multiple instances of a variable appear in the template definition, as one changes, others will update to reflect that change. However, sometimes we might want a slight variation of a specific variable. Say for instance, we are create a template to define a property with a backing field (this template already exists by the way). In this scenario, usually the backing field name is the same as that of the property name with a lowerCamelCase. Let’s see how we can define a template like this using the Macros in the Live Editor Template:

1. Create a new template following the steps outlined previously, ending up with the following definition:

12

If we were to use this template as is, ReSharper would prompt us to enter a value for $className$, $IdType$ and $backingField$. Ideally what we would like is to have the latter to be a variation of the $className$, concretely, a lowercase version.

2. On the right side of the template editor, there is a Window that displays Macros that we can apply to variables. There is one for each variable. Click on the backingField entry and select the Choose Macro link

13

A dialog box appears giving us a series of operations we can perform on the variable, from suggestions, conversions, metadata, etc. In our case we need to scroll right to the end and select “Value of another variable with the first character in lower case”:

14

3. Once we click OK, the Macro window will then be updated to reflect the change and allow us to define the value of the variable. Click “another variable” link and select one className.

15

4. Save the template.

5. Invoke the template by typing “be” as before. We should end up with:

16

We can now type the className. As we do, we see that the backing field will automatically update to reflect the changes.

17

This is just a simple case of applying a Macro. We can have multiple Macros in the same template and combine them to achieve powerful templates.

Additional Template Options

At the top of the template editor, there are two additional check boxes:

18

The first one is used to indicate whether we want the template to be reformatted according to the defined styles we have set up in ReSharper once completed. The second option is useful when we are referencing namespaces that are not in the using directives. Let’s imagine for instance that we generate a template to spit out some Unit Test fixtures for NUnit. If we were to decorate the class with a [TestCase] attribute in a blank file, ReSharper would indicate an error saying the the namespace is not imported. The way to solve this issue is to fully qualify the attribute when defining the template and select this checkbox. ReSharper will then make sure it imports the correct namespaces and shorten the attribute.

19

 

Sharing Templates

Any template created under the User Templates is visible to the current Windows user for any solution. If we want to have templates specific to a particular solution, we would need to create them under the Personal Solution Templates. These however again are restricted to the current user. If we want them shared across multiple users we need to place them under Shared Solution Templates.

When it comes to sharing templates across machines with team members, the cumbersome way would be to export templates and have each team member import it. The preferable way is to place these templates under network drive and then mount them using the Mount File Storage

20

By doing this, ReSharper is aware that these templates are shared across machines and makes sure they are updated accordingly.

Summary

Live Templates in ReSharper offer us a lot of opportunities to be more effective when writing code. It is not about code generation but about not wasting time having to type repetitive things. It is also important to understand that they should not replace good programming practices. For example, using templates to write out repetitive code for logging is not the best way to approach the problem. A better solution would be to use Aspect Orientated Programming. However, there are many case where templates are very efficient. In future blog posts we’ll examine the Surround and File templates that ReSharper also offers us.

Advanced scenarios with dotCover Console Runner

In an earlier post, we saw how to use dotCover in simple scenarios, whereby we have a single unit test project for which we want to obtain coverage reports on. There are times however, when this is not the case; that is, we might have multiple tests projects. It is very common (and recommended practice) to separate Unit test projects from Integration test projects, allowing us to run the former more frequently during development and the latter during an Automated Continuous Integration setup. In these cases, running the dotCover analyse command, as shown in the previous post might not be ideal.

 

Fine tuning the coverage steps

For the scenarios described, whereas we have multiple test projects, dotCover allows us to fine-tune the coverage process by offering us a series of commands that breakdown the coverage, merging and reporting into individual steps. Suppose we have the following Project Layout

 21

 

The AppCode is our application code that we want to run coverage for, and TestProject1 and TestProject2 are specific the test projects that cover AppCode. These can be Unit Test or Integration Test projects. The process we need to follow is shown in the figure below:

 

22

 

 

We run coverage on each of the tests projects by using the cover (c) command using the following configuration file (corresponds to testProject1.xml. testProject2.xml is similar)

<?xml version="1.0" encoding="utf-8"?>
<CoverageParams>
  <Executable>
    X:\Libraries\nUnit\nunit-console.exe
  </Executable>
  <Arguments>TestProject1.dll</Arguments>
    <WorkingDir>TestProject1\bin\debug</WorkingDir>
    <Output>output1.xml</Output>
    <Filters>
      <IncludeFilters>
        <FilterEntry>
          <ModuleMask>AppCode</ModuleMask>
          <ClassMask>*</ClassMask>
          <FunctionMask>*</FunctionMask>
        </FilterEntry>
      </IncludeFilters>
      <ExcludeFilters>
      </ExcludeFilters>
    </Filters>
</CoverageParams>

[Command to execute: dotcover c testProject1.xml]

Executing this command generates a snapshot file with coverage data, the Coverage Results Descriptor (by default, unless specified in the configuration file using the TempDir element, the snapshots are saved in the User temporary folder). These snapshots are not the final XML report that we are after but intermediate information that dotCover uses to generate reports from. There is a correspondence between snapshots and processes, such that each process generates a single snapshot. In our case, since we are calling a unit testing framework to launch a single test project, there will only be one snapshot. However there are cases, for instance when running coverage report on an application, whereby the application can launch another process  itself. In this case, multiple snapshots will be generated.

When we run this command, once for each Project, we end up with two snapshot files. We now need to merge the snapshots in order to generate the XML report. This is accomplished using the merge (m) command

<?xml version="1.0" encoding="utf-8"?>
<MergeParams>
  <Source>
    <string>output1.xml</string>
    <string>output2.xml</string>
  </Source>
  <Output>output_merged.xml</Output>
</MergeParams>

[Command to execute: dotcover m merge.xml]

which takes the output of the two cover commands (output1.xml and output2.xml) and generates a single snapshot file. The only thing remaining is to generate an XML coverage report based off of this data, using the report command

<?xml version="1.0" encoding="utf-8"?>
<ReportParams>
  <Source>output_merged.xml</Source>
  <Output>results_merged.xml</Output>
</ReportParams>

[Command to execute: dotcover r report.xml]

providing us with the XML report containing coverage information (percentage of code covered, statements covered), which like in the previous post we can apply an XSLT to format into HTML or JSON.

Other Coverage Commands

dotCover provides two more commands:

  • list
  • delete

The list command simply obtains a list of all snapshot files given the coverage descriptors, that is, the output of the different cover commands. The configuration file lists these descriptors:

<?xml version="1.0" encoding="utf-8"?>
<ListParams>
  <Source><!– Required. At least one child element expected. –>
    <string><!– Coverage results descriptor 1 file name –></string>
    <string><!– Coverage results descriptor 2 file name –></string>
    <string><!– Coverage results descriptor N file name –></string>
  </Source>
  <Output><!– Required. Resulting file name. Stores plain list of all snapshot files. –></Output>
</ListParams>

 

The delete command deletes the list of given snapshots:

<?xml version="1.0" encoding="utf-8"?>
<DeleteParams>
&nbs
p; <Source><!– Required. At least one child element expected. –>
    <string><!– Coverage results descriptor 1 file name –></string>
    <string><!– Coverage results descriptor 2 file name –></string>
    <string><!– Coverage results descriptor N file name –></string>
  </Source>
</DeleteParams>

 

Summary

As we can see, dotCover allows us to fine-tune the coverage process by letting us control the different steps. One question that might arise is why couldn’t we have done the same by running the analyse command twice, once for each Test Project. If we were to do that, we would get two independent reports, whereas here the results are all merged into a single report. The analyse command is a great fit for simple scenarios that only have a single project. Once we move onto more complex setups, having a finer control over the coverage process has its benefits.