1

I'm mocking VSTO objects and in one project (I didn't write) it has this code:

var listOfSheets = new List<Worksheet>();
var mockSheets = Substitute.For<Sheets>();
mockSheets.Count.Returns(listOfSheets.Count);

The Intellisense ToolTip for the mockSheets shows 6 properties:

NSubstitute All Properties

The line with the break point works in this project.

However I have the same code in a different project (same references, namespaces, etc.), yet the Intellisense ToolTip for the mockSheets only shows 1 property:

NSubstitute Only one Property

I know this is the root cause that I'm trying to solve, but fyi the actual problem is:

Cannot perform runtime binding on a null reference

Cannot perform runtime binding on a null reference

EDIT:

The Sheet Object is mocked:

public static Worksheet Sheet
{
    get
    {
        var mockSheet = Substitute.For<Worksheet>();
        mockSheet.Name = MockSheetName;
        mockSheet.Visible = XlSheetVisibility.xlSheetVisible;

        return mockSheet;
    }
}

public static Workbook Workbook()
{
    return Workbook(1);
}
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
  • 1
    If you manually mock `Sheets` (created a `FakeSheets` class that derives from `Sheets`) does it work as expected? – David Tchepak Mar 22 '12 at 21:32
  • 1
    @DavidTchepak Hi David, thanks for picking me up on that point, yes Sheets is already fakemocked. Please see my edit. I think that there is an underlying reason that the properties aren't showing, do you know any debugging techniques to uncover that? Actually I'll download the NSubstitute sourcecode and use that rather than release DLL – Jeremy Thompson Mar 22 '12 at 21:34
  • 1
    @DavidTchepak I dont have admin rights (at work) and I cant install NuGet:( I dont even have reflector so I cant even decompile the dll:( Would you know of any other way to get the source code? – Jeremy Thompson Mar 22 '12 at 22:15
  • 1
    I meant if you manually create fakes (i.e. without using NSubstitute) does it work as expected? If you want the source you can grab it from https://github.com/nsubstitute/NSubstitute/downloads (can download w/out checking it out), or try ilspy to decompile (pretty sure it doesn't need admin). – David Tchepak Mar 22 '12 at 23:48

4 Answers4

1

This is a reminder to myself everytime I write Nsubstitute Excel Unit Tests. I have battled with this error too many times.

You will get the error: Cannot perform runtime binding on a null reference

When you have referened the .Net Excel Object Library, you MUST reference the COM Microsoft Excel 14.0 Object Library. Once the COM interop Excel DLL is referenced, click F4 to see the DLLs Properties, remember to set the COM Interop NOT to Embed Interop Types.

.Excel Here is a working Project file:

<ItemGroup>
    <Reference Include="Microsoft.Office.Interop.Excel.Extensions">
      <HintPath>..\..\Refs\Microsoft.Office.Interop.Excel.Extensions.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
    <Reference Include="NSubstitute">
      <HintPath>..\..\Refs\NSubstitute.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core">
      <RequiredTargetFramework>3.5</RequiredTargetFramework>
    </Reference>
    <Reference Include="System.Data" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Runtime.Serialization" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="UIAutomationProvider" />
    <Reference Include="VSTOContrib.Core, Version=0.9.0.52, Culture=neutral, processorArchitecture=MSIL" />
    <Reference Include="WindowsBase" />
    <Reference Include="WindowsFormsIntegration" />
  </ItemGroup>
  <ItemGroup>
    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
      <Visible>False</Visible>
    </CodeAnalysisDependentAssemblyPaths>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="MockFactory.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="UnitTests.cs" />
  </ItemGroup>
  <ItemGroup>
      <COMReference Include="Microsoft.Office.Core">
      <Guid>{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}</Guid>
      <VersionMajor>2</VersionMajor>
      <VersionMinor>4</VersionMinor>
      <Lcid>0</Lcid>
      <WrapperTool>primary</WrapperTool>
      <Isolated>False</Isolated>
    </COMReference>
    <COMReference Include="Microsoft.Office.Interop.Excel">
      <Guid>{00020813-0000-0000-C000-000000000046}</Guid>
      <VersionMajor>1</VersionMajor>
      <VersionMinor>6</VersionMinor>
      <Lcid>0</Lcid>
      <WrapperTool>primary</WrapperTool>
      <Isolated>False</Isolated>
    </COMReference>
  </ItemGroup>

The offender is this .Net Interop reference (needs to be the COM reference):

<Reference Include="Microsoft.Office.Interop.Excel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL">
  <EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
1

This is a wild guess, but Office Interop arrays are 1 based, not 0 based. I havent looked into it but this may be defined in the metadata. Try this:

for (int i = 0; i < numSheets; i++)
{
    listOfSheets.Add(Sheet);
    listOfSheets[i].Name = MockSheetName + (i + 1);
    `mockSheets[i + 1].Returns(listOfSheets[i]);`
}
Jake Ginnivan
  • 2,112
  • 16
  • 20
  • 1
    +1 thanks Jake. It wasn't the fix but any advice you can give is much appreciated. Please see edit, my Print Screen works today:) – Jeremy Thompson Mar 22 '12 at 20:30
1

Some history:

I was getting a compile issue with these errors:

1.Predefined type 'Microsoft.CSharp.RuntimeBinder.Binder' is not defined or imported
2.One or more types required to compile a dynamic expression cannot be found. Are you missing references to Microsoft.CSharp.dll and System.Core.dll?

So I found this article and referenced the Microsoft.CSharp library: C# 4.0 and .Net 3.5

To the point: The class I've been showing essentially Mocks the Excel Object Model and I copied this class from one project to the other (I cant reference the other project as its actually separate project plus it would have caused circular dependency) I found the Returns extension method was listed in intellisense but I got a "Could not resolve Symbol" when compiling. Even though going to Definition was the same in both classes/projects. To get around this initially I did the commented out lines:

public static Range Cell
{
    get
    {
        var mockCell = Substitute.For<Range>();
        mockCell.Address.Returns("$A$1");
        mockCell.Formula = "=1+1";
        mockCell.ToString().Returns(mockCell.Formula.ToString());
        //mockCell.ToString().Returns(info => mockCell.Formula.ToString());
        //SubstituteExtensions.Returns(mockCell.ToString(),  mockCell.Formula.ToString());
        mockCell.Worksheet.Returns(Sheet);
        mockCell.Worksheet.Name.Returns(MockSheetName);

        return mockCell;
    }
}

This point is a bit of a red-herring but removing the Microsoft.CSharp dll actually allowed the Returns extension method to resolve successfully. Then I found removing the Microsoft.CSharp dll resolved my problem, it all just worked, the mockSheet object has all its properties and was able to execute successfully without the "Cannot perform runtime binding on a null reference" error.

Oh and a tip for anyone Mocking Interop Types, be extra careful to set this:

enter image description here

Community
  • 1
  • 1
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
1

Sounds like the following may be the cause:

VSTO Specific Project to be tested (Addin/taskpane/etc)
VSTO ver: VSTO 3.0 SP1
.NET ver: .NET 3.5 SP1

VS 2010 Test project for project above
defaults to
.NET Ver: .NET 4.0

This would create a referencing problem when going to mock up the objects as the test project is expecting to be able to use MS.Csharp (i guess) and possibly other references.

So the exception is not really refering to the Null value returned by the mock object at all, but rather a null binding exception caused by not being able to load .NET 4.0's CSharp library.

Therefore as you discovered the solution is to remove .net 4.0 Csharp reference. It is perhaps unnecessary although to set the project to run under .net 3.5? Not sure you would have to test if any other problems occur. But I guess better to keep test projects on .net 4 if you can. Unless someone can indicate if this is not best practise.

Anonymous Type
  • 3,051
  • 2
  • 27
  • 45