es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Por qué no devuelve lo que se supone que debe devolver una mock (Moq) en C#?

Tengo el siguiente código que quiero probar:

// Snipped for brevity

public DocuSignCallbackHandler(
                ContractDAO contractDAO,
                ContractLister contractLister,
                SalesforceOpportunityProvider salesforceOpportunityProvider,
                IEnvelopeService docuSignEnvelopeService,
                IHttpContextAccessor httpContextAccessor,
                IImageSaver imageSaveClient,
                ISalesforceCacheSyncDataManipulator salesforceCacheSyncDataManipulator,
                SalesforceContractResolver salesforceContractResolver,
                SfContractWriteDataFactory sfContractWriteFactory
            )
        {
            _contractDAO = contractDAO ?? throw new ArgumentNullException(nameof(contractDAO));
            _contractLister = contractLister ?? throw new ArgumentNullException(nameof(contractLister));
            _salesforceOpportunityProvider = salesforceOpportunityProvider ?? throw new ArgumentNullException(nameof(salesforceOpportunityProvider));
            _docuSignEnvelopeService = docuSignEnvelopeService ?? throw new ArgumentNullException(nameof(docuSignEnvelopeService));
            _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
            _imageSaveClient = imageSaveClient ?? throw new ArgumentNullException(nameof(imageSaveClient));
            _salesforceCacheSyncDataManipulator = salesforceCacheSyncDataManipulator ?? throw new ArgumentNullException(nameof(salesforceCacheSyncDataManipulator));
            _salesforceContractResolver = salesforceContractResolver ?? throw new ArgumentNullException(nameof(salesforceContractResolver));
            _sfContractWriteFactory = sfContractWriteFactory ?? throw new ArgumentNullException(nameof(sfContractWriteFactory));
        }

    public async Task<contractbase> UpdateDocuSignStatus(string contractNumber, CancellationToken cancellationToken)
    {
        ContractBase contract = await _contractLister.GetByContract(contractNumber)
            ?? throw new IllegalContractOperationException($"Service does not have a contract numbered { contractNumber }.");

        DocusignPdfResult docuSignPdfResult = await GetPDFfromDocuSign(contract, 0, cancellationToken);

        contract = docuSignPdfResult.ImageLoadId != ""
            ? AddSignature(contract, docuSignPdfResult)
            : throw new IllegalContractOperationException($"Result is in an unexpected state: StatusCode: {docuSignPdfResult.StatusCode}, ImageLoadId: {docuSignPdfResult.ImageLoadId}");

        contract.SfCacheCtrId = docuSignPdfResult.SalesforceContractId;

        ContractBase result = await _contractDAO.Update(contract, cancellationToken);

        // NOTE: In real life, this works, 
        // but in my unit tests, this value is always null.
        return result;
    }

// Snipped for brevity

Mi prueba se ve así:

[Fact]
        public async Task TestUpdateDocuSignStatusShouldMarkContractsSigned()
        {
            // Arrange
            AutoMocker mocker = new();
            DocuSignCallbackHandler handlerUnderTest = mocker.CreateInstance<docusigncallbackhandler>();
            ContractBase testContract = new()
            {
                ContractNumber = ContractNumber,
                EnvelopeId = EnvelopeId,
                ContractHistory = new()
                {
                    new()
                    {
                        User = "Fred"
                    }
                }
            };
            mocker.GetMock<contractlister>()
                .Setup(x => x.GetByContract(ContractNumber))
                .ReturnsAsync(testContract);
            mocker.GetMock<ienvelopeservice>()
                .Setup(x => x.GetEnvelope(EnvelopeId, It.IsAny<><sessionurls>>()))
                .ReturnsAsync(new MemoryStream());
            mocker.GetMock<iimagesaver>()
                .Setup(x => x.Save(It.IsAny<imageenvelope>(), CancellationToken))
                .ReturnsAsync(new HttpResponseMessage()
                {
                    StatusCode = HttpStatusCode.OK,
                    Content = new StringContent(JsonSerializer.Serialize(new List<imageuploadersaveresponse>() {
                        new()
                        {
                            Id = ImageLoadId
                        }
                    }))
                });
            SfOpportunity testOpportunity = new()
            {
                AccountId = AccountId
            };
            mocker.GetMock<salesforceopportunityprovider>()
                .Setup(x => x.GetOpportunity(testContract, CancellationToken))
                .ReturnsAsync(testOpportunity);
            SfContract testOutboundContract = new();
            mocker.GetMock<sfcontractwritedatafactory>()
                .Setup(x => x.CreateFor(testContract, testOpportunity, It.IsAny<imageuploadersaveresponse>()))
                .Returns(testOutboundContract);
            mocker.GetMock<salesforcecontractresolver>()
                .Setup(x => x.ToJObject(testOutboundContract))
                .Returns(new JObject());
            List<saveresponse> testSaveResponses = new()
            {
                new()
            };
            mocker.GetMock<isalesforcecachesyncdatamanipulator>()
                .Setup(x => x.Insert(It.IsAny<><salesforcerecord>>(), It.IsAny<datamanipulationoptions>(), CancellationToken))
                .ReturnsAsync(testSaveResponses);

        ContractBase finalContract = new()
        {
            ContractNumber = "2325235325"
        };

        // NOTE: This mock seems to be ignored.
        mocker.GetMock<contractdao>()
            .Setup(x => x.Update(It.IsAny<contractbase>(), It.IsAny<cancellationtoken>()))
            .ReturnsAsync(finalContract);

        // Act
        ContractBase resultContract = await handlerUnderTest.UpdateDocuSignStatus(ContractNumber, CancellationToken);

        // Assert
        Assert.Equal(finalContract, resultContract);
    }

Por lo general, Moq funciona como se espera, y mis métodos de dependencia simulados devuelven los valores que quiero.

Sin embargo, el último simulacro dentro de la sección “organizar” parece ignorarse.
Como en la encarnación actual, aprovechando It.IsAny () para ambos parámetros, esperaría que siempre devuelva el valor de finalContract, pero de hecho, el valor de retorno de ContractDAO.Update () siempre es nulo, exactamente igual que si dejo ese simulacro fuera del código de prueba.

Por lo que vale, el método concreto que quiero simular se ve así:

// Snipped for brevity

public ContractDAO(ContractDatabaseSettings contractDatabaseSettings, ILogger<contractdao> logger, IMongoDatabase mongoDatabase)
        {
            _contractDatabaseSettings = contractDatabaseSettings ?? throw new ArgumentNullException(nameof(contractDatabaseSettings));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _mongoDatabase = mongoDatabase ?? throw new ArgumentNullException(nameof(mongoDatabase));
        }

    public async Task<contractbase> Update(ContractBase inputContract, CancellationToken cancellationToken)
    {
        try
        {
            await GetCollection()
                .ReplaceOneAsync(
                    contract => contract.Id == inputContract.Id,
                    inputContract,
                    cancellationToken: cancellationToken
            );
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Tryng to save data for Contract {ContractNumber}.", inputContract.ContractNumber);
            return null;
        }
        return inputContract;
    }

    private IMongoCollection<contractbase> GetCollection() =>
                _mongoDatabase.GetCollection<contractbase>(_contractDatabaseSettings.ContractCollectionName);

// Snipped for brevity

Como sugirió @Ermiya Eskandary, puedo arreglar esto cambiando el orden en que se simulan las cosas, por ejemplo, haciendo que esto sea lo primero en lugar de lo último simulado, así:

        [Fact]
        public async Task TestUpdateDocuSignStatusShouldMarkContractsSigned()
        {
            // Arrange
            AutoMocker mocker = new();
            ContractBase finalContract = new()
            {
                ContractNumber = "2325235325"
            };

            mocker.GetMock<contractdao>()
                .Setup(x => x.Update(It.IsAny<contractbase>(), It.IsAny<cancellationtoken>()))
                .ReturnsAsync(finalContract);

// etc.

¿Cómo es esta dependencia diferente de cualquier otra dependencia, para la cual la simulación siempre funciona como se espera?

¿Por qué debería importar el orden?
Dado que este es el último simulacro que se aplicará, ¿por qué debería ser enumerado primero (o al menos, no como último)?

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    El problema no está relacionado con la dependencia en sí misma, sino con el orden en que se falsifican las dependencias. La última falsificación es la que está siendo ignorada, lo que significa que podría haber otra falsificación que la esté anulando.

    Es posible que alguna otra dependencia también esté usando It.IsAny() para ambos parámetros y se esté configurando después de la falsificación de ContractDAO.Update(), anulándola. Al reorganizar el orden de configuración de la falsificación, la falsificación de ContractDAO.Update() se convierte en la primera en lugar de la última, asegurando que no sea anulada por ninguna falsificación posterior.

    El orden sí importa cuando se trata de configurar falsificaciones utilizando It.IsAny() u otros emparejadores genéricos. Se recomienda configurar falsificaciones más específicas primero, seguidas de las más genéricas para evitar comportamientos inesperados.

Comments are closed.