Skip to content

Unit tests : Make all concrete class mockable #5714

@NicolasREY69330

Description

@NicolasREY69330

Hi,

I need to mock some methods and properties in Nest.IElasticClient interface that is used in my repo. As example I need to stub the Indices.Exists() method to return an ExistsResponse having an Exists property returning true.

The problem is that the concrete class has no interface implementation, nor setter on the Exists property, and is not declared virtual neither in Nest lib:

public class ExistsResponse : ResponseBase
{
    public ExistsResponse();
    public bool Exists { get; }
}

public ExistsResponse Exists(Indices index, Func<IndexExistsDescriptor, IIndexExistsRequest> selector = null);

So for the mocking I tried to set the property anyway on the concrete class, but it failed with all the following methods, I have no idea on how to do ...

/* Fail with exception :
System.NotSupportedException : Unsupported expression: x => x.Exists
    Non-overridable members (here: ExistsResponse.get_Exists) may not be used in setup / verification expressions.
*/
    var mock1 = new Mock<ExistsResponse>();
    obj.SetupGet(f => f.Exists).Returns(true);

/* Fail with exception :
 System.NotSupportedException : Unsupported expression: f => f.Exists
    Non-overridable members (here: ExistsResponse.get_Exists) may not be used in setup / verification expressions.
*/
    var mock2 = Mock.Of<ExistsResponse>(x => x.Exists == true);

/* Fail with exception :
System.ArgumentException : Property set method not found.
*/
    var mock3 = new ExistsResponse();
    var property = typeof(ExistsResponse).GetProperty("Exists", BindingFlags.Public | BindingFlags.Instance);
    property.SetValue(mock3, true);

/* Fail with exception :
System.NullReferenceException (setter is null)
*/
    var mock4 = new ExistsResponse();
    var setter = property.GetSetMethod(true);
    setter.Invoke(mock4, new object[] { true });


    // My Mock on the Indices.Exists method
    var elasticMock = new Mock<IElasticClient>();
    elasticMock
    .Setup(x => x.Indices.Exists(It.IsAny<string>(), null))
    .Returns(/*** my stubbed object here ***/); // <== how to stub the concrete class to return a ExistsResponse.Exists = true ?

var repositoryMock = new Mock<GeocodedCityElkRepository>(elasticMock.Object, new NullLogger<GeocodedCityElkRepository>());
            repositoryMock
                .Setup(x => x.Get(It.IsAny<int>(), It.IsAny<Func<QueryContainerDescriptor<GeocodedCity>, QueryContainer>>()))
                .Returns(Task.FromResult(new List<GeocodedCity>().AsReadOnly() as IReadOnlyCollection<GeocodedCity>));
     
            // Act
            var exception = await Assert.ThrowsAsync<GeocodingException>(() =>
                repositoryMock.Object.GetDensity("11111", "city", "FR"));

Well unless I missed something, I'm stuck ...

Describe the solution you'd like
Allow to mock concrete class (making property virtual) or at least provide interface for returned objects like ExistsResponse.

    public interface IExistsResponse
    {
        bool Exists { get; }
    }

    [DataContract]
    public class ExistsResponse : ResponseBase, IExistsResponse
    {
        public ExistsResponse();
        public bool Exists { get; }
    }

Describe alternatives you've considered
Do not test my repository ...

Additional context

My libs :

Nest 7.12.1
Moq 4.15.2
XUnit 2.4.1
.Net 5

Here is a subset of my repo

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions