添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

An Xperience project I'm working on contains the following interface:

public interface IContentRepository
    Task<IEnumerable<T>> GetWebPageAsync<T>(
       ContentItemQueryBuilder builder,
       Cancellationtoken cancellationToken
    ) where T : new();

The implementation of this is:

public class ContentRepository : IContentRepository
   private readonly IContentQueryExecutor _contentQueryExecutor;
   private readonly IWebPageQueryResultMapper _webPageQueryResultMapper;
   private readonly IWebsiteChannelContext _websiteChannelContext;
   public ContentRepository(
      IContentQueryExecutor contentQueryExecutor,
      IWebPageQueryResultMapper webPageQueryResultMapper,
      IWebsiteChannelContext websiteChannelContext,
       _contentQueryExecutor = contentQueryExecutor;
       _webPageQueryResultMapper = webPageQueryResultMapper;
       _websiteChannelContext = websiteChannelContext;
   public async Task<IEnumerable<T>> GetWebPageAsync<T>(
      ContentItemQueryBuilder builder,
      CancellationToken cancellationToken
   ) where T : new()
       var queryOptions = new ContentQueryExecutionOptions()
          ForPreview = _websiteChannelContext.IsPreview
       return await _contentQueryExecutor.GetWebPageResult(
          builder: builder,
          resultSelector: _webPageQueryResultMapper.Map<T>,
          options: queryOptions,
          cancellationToken: cancellationToken

The idea behind this repository is that you can just do a single call to get web pages, content and such. For testing the method above I need to mock the _contentQueryExecutor.GetWebPageResult call to return an IEnumerable of the given page type. However nothing really seems to work. In my test file for example I do the following:

var homePage = new HomePage
   SystemFields = new WebPageFields
       WebPageItemID = 1
_webPageQueryResultMapper
   .Map<HomePage>(Arg.Any<IWebPageContentQueryDataContainer>())
   .Returns(homePage);
_contentQueryExecutor
    .GetWebPageResult(
        builder: Arg.Any<ContentItemQueryBuilder>(),
        resultSelector: _webPageQueryResultMapper.Map<HomePage>,
        options: Arg.Any<ContentQueryExecutionOptions>(),
        cancellationToken: Arg.Any<CancellationToken>()
    .Returns([homePage]);

But the result always seems to be null from the _contentQueryExecutor. The _webPageQueryResultMapper returns the correct value however. I'm using NSubstitute and xUnit for my unit tests.

@IvanPeev

I can't speak to xUnit compatibility since our unit and integration testing support is designed only for NUnit.

I re-created your example test in Xperience v28.4.1 using NUnit.

dotnet new update
dotnet new kentico-xperience-sample-mvc -n DancingGoat -o xk-28-04-01-01\DancingGoat
cd xk-28-04-01-01
dotnet new nunit -n DancingGoat.Tests -o .\DancingGoat.Tests
dotnet add .\DancingGoat.Tests\ reference .\DancingGoat\
dotnet add .\DancingGoat.Tests\ package NSubstitute

Then I added the test code:

using CMS.ContentEngine;
using CMS.Websites;
using DancingGoat.Models;
using NSubstitute;
namespace DancingGoat.Tests;
public class Tests
    [Test]
    public void Test1()
        var _webPageQueryResultMapper = Substitute.For<IWebPageQueryResultMapper>();
        var _contentQueryExecutor = Substitute.For<IContentQueryExecutor>();
        var homePage = new HomePage
            SystemFields = new WebPageFields
                WebPageItemID = 1
        _webPageQueryResultMapper
           .Map<HomePage>(Arg.Any<IWebPageContentQueryDataContainer>())
           .Returns(homePage);
        _contentQueryExecutor
            .GetWebPageResult(
                builder: Arg.Any<ContentItemQueryBuilder>(),
                resultSelector: _webPageQueryResultMapper.Map<HomePage>,
                options: Arg.Any<ContentQueryExecutionOptions>(),
                cancellationToken: Arg.Any<CancellationToken>()
            .Returns([homePage]);

Running the tests results in a passing test

dotnet test
Microsoft (R) Test Execution Command Line Tool Version 17.9.0 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: 53 ms - DancingGoat.Tests.dll (net8.0)

Maybe there's some incompatibility with Xunit? I know I ran into issues with it in the past when developing for previous versions of Kentico.

Or maybe there was an unidentified bug with the version of Xperience by Kentico you are using that has been patched as of v28.4.1?

@seangwright

So in the example above in test method the given functions are mocked which is syntactically correct and the test passes! It's exactly what you'd expect. However the real issue starts when you do the actual calls to the mocked functions to verify that they return the correct values.

Let's extend the test method by adding a couple of calls and storing them in variables:

[Fact]
public async void Test1()
    var _webPageQueryResultMapper = Substitute.For<IWebPageQueryResultMapper>();
    var _contentQueryExecutor = Substitute.For<IContentQueryExecutor>();
    var homePage = new HomePage
        SystemFields = new WebPageFields
            WebPageItemID = 1
    _webPageQueryResultMapper
       .Map<HomePage>(Arg.Any<IWebPageContentQueryDataContainer>())
       .Returns(homePage);
    _contentQueryExecutor
        .GetWebPageResult(
            builder: Arg.Any<ContentItemQueryBuilder>(),
            resultSelector: _webPageQueryResultMapper.Map<HomePage>,
            options: Arg.Any<ContentQueryExecutionOptions>(),
            cancellationToken: Arg.Any<CancellationToken>()
        .Returns([homePage]);
    var testBuilder = new ContentItemQueryBuilder().InLanguage("en");
    HomePage mappedResult = _webPageQueryResultMapper.Map<HomePage>(null);
    IEnumerable<HomePage> result = await _contentQueryExecutor
        .GetWebPageResult(
            builder: testBuilder,
            resultSelector: _webPageQueryResultMapper.Map<HomePage>,
            options: null,
            cancellationToken: CancellationToken.None

When I debug the test method I see that the value in the mappedResult variable is correct, it returns an instance of HomePage. However the result variable is an empty enumerable. That's the issue that I've been having, that it's just difficult to mock the IContentQueryExecutor.

I've also tried this out in v28.4.1, but it yields the same result. It could genuinely be the case that using xUnit is what's causing this problem. I'm curious to see whether this actually works in NUnit.