AYUDA .NET

Moq C# (Cómo funciona para desarrolladores)

Publicado en 29 de octubre, 2023
Compartir:

En el mundo del desarrollo de software, las pruebas son un proceso indispensable. Garantiza que el código funcione como se espera y ayuda a detectar errores antes de que lleguen a producción. Un aspecto vital de las pruebas es la simulación, y cuando se trata de pruebas de C#, MOQ es una poderosa herramienta en el arsenal de un desarrollador. Ofrece soporte para expresiones lambda. MOQ, abreviatura de "Mock Object Framework for .NET", simplifica el proceso de creación de objetos simulados para pruebas unitarias. En este artículo, profundizaremos en MOQ en C#.

¿Qué es el MOQ?

MOQ - Marco de simulación para .NET es un marco de trabajo de simulación para aplicaciones .NET que permite a los desarrolladores crear objetos simulados de forma rápida y eficaz. Los objetos simulados simulan el comportamiento de objetos reales en su aplicación, lo que facilita el aislamiento y la comprobación de partes específicas de su código. MOQ simplifica el proceso de crear y trabajar con estos objetos simulados.

Características principales de MOQ

  • Interfaz fluida: MOQ proporciona una API fluida y expresiva para establecer expectativas y verificaciones. Esto hace que el código de prueba sea más legible y fácil de entender.
  • Tipificación fuerte: MOQ aprovecha las características del lenguaje C# para proporcionar una tipificación fuerte y soporte IntelliSense al definir mocks y expectativas. Esto reduce las posibilidades de que se produzcan errores de ejecución en sus pruebas.
  • **MOQ soporta tanto el mocking estricto como el loose. Loose mocking permite crear objetos simulados que responden a cualquier llamada de método, mientras que strict mocking impone que sólo se llame a los métodos esperados.
  • Comportamiento Verificable: MOQ le permite verificar que métodos específicos en sus objetos simulados fueron llamados con los argumentos esperados y en el orden correcto.
  • Devoluciones: Puedes definir devoluciones de llamada para ejecutar código personalizado cuando se llama a un método simulado y especificar valores de retorno para métodos simulados.

Primeros pasos con MOQ

En este tutorial, exploraremos cómo utilizar MOQ, un popular framework de mocking para C#, para facilitar las pruebas unitarias. Veremos un ejemplo en el que crearemos y probaremos un escenario de transacción ATM simple utilizando MOQ para simular dependencias.

Crear un nuevo C

Siga los siguientes pasos para Crear un nuevo proyecto

  1. AbrirVisual Studio, vaya a "Archivo" > "Nuevo" > "Proyecto...".

  2. Elija una plantilla de proyecto, configure los ajustes y haga clic en "Crear".

    Moq C#(Cómo funciona para desarrolladores) Figura 1 - Crear una nueva aplicación de consola en Visual Studio 2022

    Supongamos que está desarrollando software para un cajero automático(Cajeros automáticos)y tiene que probar las funciones de autenticación y retirada. El ATM depende de dos interfaces: IHostBank e IHSMModule. Queremos probar la clase ATMCashWithdrawal, que representa la funcionalidad de retirada de efectivo del cajero automático.

    Crea dos interfaces, IHostBank e IHSMModule, que representan las dependencias del sistema ATM. Definir métodos relevantes como authenticateAmount y validatePIN.

// IHostBank.cs
public interface IHostBank
{
    bool AuthenticateAmount(string accountNumber, int amount);
}

// IHSMModule.cs
public interface IHSMModule
{
    bool ValidatePIN(string cardNumber, int pin);
}
// IHostBank.cs
public interface IHostBank
{
    bool AuthenticateAmount(string accountNumber, int amount);
}

// IHSMModule.cs
public interface IHSMModule
{
    bool ValidatePIN(string cardNumber, int pin);
}
' IHostBank.cs
Public Interface IHostBank
	Function AuthenticateAmount(ByVal accountNumber As String, ByVal amount As Integer) As Boolean
End Interface

' IHSMModule.cs
Public Interface IHSMModule
	Function ValidatePIN(ByVal cardNumber As String, ByVal pin As Integer) As Boolean
End Interface
VB   C#

Cree la clase ATMCashWithdrawal, que utiliza las dependencias mencionadas para realizar operaciones de cajero automático. En esta clase, implementará un determinado método como WithdrawAmount.

// ATMCashWithdrawal.cs
public class ATMCashWithdrawal
{
    private readonly IHSMModule hsmModule;
    private readonly IHostBank hostBank;

    public ATMCashWithdrawal(IHSMModule hsmModule, IHostBank hostBank)
    {
        this.hsmModule = hsmModule;
        this.hostBank = hostBank;
    }
// non static method
    public bool WithdrawAmount(string cardNumber, int pin, int amount)
    {
        if (!hsmModule.ValidatePIN(cardNumber, pin))
        {
            return false;
        }

        if (!hostBank.AuthenticateAmount(cardNumber, amount))
        {
            return false;
        }

        // Withdraw the specified amount and perform other operations
        return true;
    }
}
// ATMCashWithdrawal.cs
public class ATMCashWithdrawal
{
    private readonly IHSMModule hsmModule;
    private readonly IHostBank hostBank;

    public ATMCashWithdrawal(IHSMModule hsmModule, IHostBank hostBank)
    {
        this.hsmModule = hsmModule;
        this.hostBank = hostBank;
    }
// non static method
    public bool WithdrawAmount(string cardNumber, int pin, int amount)
    {
        if (!hsmModule.ValidatePIN(cardNumber, pin))
        {
            return false;
        }

        if (!hostBank.AuthenticateAmount(cardNumber, amount))
        {
            return false;
        }

        // Withdraw the specified amount and perform other operations
        return true;
    }
}
' ATMCashWithdrawal.cs
Public Class ATMCashWithdrawal
	Private ReadOnly hsmModule As IHSMModule
	Private ReadOnly hostBank As IHostBank

	Public Sub New(ByVal hsmModule As IHSMModule, ByVal hostBank As IHostBank)
		Me.hsmModule = hsmModule
		Me.hostBank = hostBank
	End Sub
' non static method
	Public Function WithdrawAmount(ByVal cardNumber As String, ByVal pin As Integer, ByVal amount As Integer) As Boolean
		If Not hsmModule.ValidatePIN(cardNumber, pin) Then
			Return False
		End If

		If Not hostBank.AuthenticateAmount(cardNumber, amount) Then
			Return False
		End If

		' Withdraw the specified amount and perform other operations
		Return True
	End Function
End Class
VB   C#

Crear proyecto de prueba unitaria

Ahora, vamos a crear pruebas unitarias para la clase ATMCashWithdrawal utilizando MOQ para simular las dependencias.

Cree un nuevo Proyecto de Pruebas Unitarias en su solución y nómbrelo como ATMSystem.Tests.

Para añadir un proyecto de prueba NUnit a su solución Visual Studio, siga estos pasos:

  1. Haga clic con el botón derecho del ratón en la solución: En el Explorador de soluciones(normalmente a la derecha)haga clic con el botón derecho del ratón en el nombre de la solución.

  2. Añadir > Nuevo proyecto: En el menú contextual, seleccione "Añadir" y, a continuación, "Nuevo proyecto...".

  3. Crear un nuevo proyecto: En el diálogo "Añadir nuevo proyecto", puedes buscar "NUnit" para encontrar las plantillas NUnit disponibles. Elija el Proyecto de Prueba NUnit como se muestra a continuación.

    Moq C#(Cómo Funciona Para Desarrolladores) Figura 2 - Añada un nuevo Proyecto de Pruebas NUnit en su solución.

  4. Configurar el proyecto: Configure los ajustes del proyecto según sea necesario, incluyendo el nombre y la ubicación del proyecto.

  5. Haga clic en Aceptar: Haga clic en el botón "Crear" o "Aceptar" para añadir el proyecto de prueba NUnit a su solución.

    Ahora, tiene un proyecto de pruebas NUnit separado dentro de su solución donde puede escribir y gestionar sus pruebas unitarias. También puede añadir referencias a los proyectos que desea probar y empezar a escribir sus casos de prueba NUnit en este proyecto.

    Para empezar a utilizar MOQ en el proyecto de prueba, tendrá que añadir el paquete MOQ NuGet a su solución. Puede hacerlo utilizando el gestor de paquetes NuGet en Visual Studio o ejecutando el siguiente comando en la consola del gestor de paquetes:

Install-package moq

Este comando instalará el paquete y añadirá todas las dependencias necesarias en el proyecto.

Escribir pruebas unitarias utilizando NUnit y MOQ para simular las dependencias.(IHostBank y IHSMModule.) de la clase ATMCashWithdrawal.

using Moq;
using MOQTestProject;

namespace UnitTest
{
    public class Tests
    {
        ATMCashWithdrawal atmCash;
        [SetUp]
        public void Setup()
        {
            // Arrange
            var hsmModuleMock = new Mock<IHSMModule>();
            hsmModuleMock.Setup(h => h.ValidatePIN("123456781234", 1234)).Returns(true);

            var hostBankMock = new Mock<IHostBank>();
            hostBankMock.Setup(h => h.AuthenticateAmount("123456781234", 500)).Returns(true);
            var atmCash = new ATMCashWithdrawal(hsmModuleMock.Object, hostBankMock.Object); // Object property
        }

        [Test]
        public void WithdrawAmount_ValidTransaction_ReturnsTrue()
        {
            // Act
            bool result = atmCash.WithdrawAmount("123456781234", 1234, 500);

            // Assert
            Assert.IsTrue(result); // Verify method 
        }

        // Add more test cases for different scenarios (e.g., invalid PIN, insufficient funds, etc.)
    }
}
using Moq;
using MOQTestProject;

namespace UnitTest
{
    public class Tests
    {
        ATMCashWithdrawal atmCash;
        [SetUp]
        public void Setup()
        {
            // Arrange
            var hsmModuleMock = new Mock<IHSMModule>();
            hsmModuleMock.Setup(h => h.ValidatePIN("123456781234", 1234)).Returns(true);

            var hostBankMock = new Mock<IHostBank>();
            hostBankMock.Setup(h => h.AuthenticateAmount("123456781234", 500)).Returns(true);
            var atmCash = new ATMCashWithdrawal(hsmModuleMock.Object, hostBankMock.Object); // Object property
        }

        [Test]
        public void WithdrawAmount_ValidTransaction_ReturnsTrue()
        {
            // Act
            bool result = atmCash.WithdrawAmount("123456781234", 1234, 500);

            // Assert
            Assert.IsTrue(result); // Verify method 
        }

        // Add more test cases for different scenarios (e.g., invalid PIN, insufficient funds, etc.)
    }
}
Imports Moq
Imports MOQTestProject

Namespace UnitTest
	Public Class Tests
		Private atmCash As ATMCashWithdrawal
		<SetUp>
		Public Sub Setup()
			' Arrange
			Dim hsmModuleMock = New Mock(Of IHSMModule)()
			hsmModuleMock.Setup(Function(h) h.ValidatePIN("123456781234", 1234)).Returns(True)

			Dim hostBankMock = New Mock(Of IHostBank)()
			hostBankMock.Setup(Function(h) h.AuthenticateAmount("123456781234", 500)).Returns(True)
			Dim atmCash = New ATMCashWithdrawal(hsmModuleMock.Object, hostBankMock.Object) ' Object property
		End Sub

		<Test>
		Public Sub WithdrawAmount_ValidTransaction_ReturnsTrue()
			' Act
			Dim result As Boolean = atmCash.WithdrawAmount("123456781234", 1234, 500)

			' Assert
			Assert.IsTrue(result) ' Verify method
		End Sub

		' Add more test cases for different scenarios (e.g., invalid PIN, insufficient funds, etc.)
	End Class
End Namespace
VB   C#

En este código de prueba, estamos utilizando MOQ para crear objetos simulados para IHSMModule y IHostBank y especificar su comportamiento cuando se llaman durante la prueba.

En el ejemplo de código anterior, hemos demostrado el concepto de imitar objetos utilizando MOQ en C#. Creamos objetos simulados para las interfaces IHSMModule e IHostBank, simulando su comportamiento durante las pruebas unitarias. Esto nos permite aislar y probar a fondo la clase ATMCashWithdrawal controlando las respuestas de estos objetos simulados. A través de la simulación, podemos asegurarnos de que nuestro código interactúa correctamente con estas dependencias, haciendo que nuestras pruebas se centren, sean predecibles y eficaces en la identificación de problemas dentro de la unidad específica de código bajo examen. Esta práctica mejora la fiabilidad general y la capacidad de mantenimiento, y facilita la comprobación del código.

Paso 3 Ejecución de las pruebas

  1. Construya su solución para asegurarse de que todo está actualizado.

  2. Abra el Explorador de pruebas en Visual Studio(Prueba > Explorador de pruebas).

  3. Haga clic en el botón "Ejecutar todo" del Explorador de pruebas para ejecutar sus pruebas unitarias.

  4. Revise los resultados de las pruebas. Deberías ver la prueba que escribiste(WithdrawAmount_ValidTransaction_ReturnsTrue) pase.

    Moq C#(Cómo funciona para los desarrolladores) Figura 3 - Para ejecutar las pruebas, primero tendrá que construir la solución. Una vez realizada la compilación, abra el Explorador de pruebas en Visual Studio y haga clic en el botón Ejecutar todo para iniciar la ejecución de las pruebas unitarias.

    De esta forma, podemos aislar el código que queremos probar y asegurarnos de que se comporta como se espera en varios escenarios mediante la simulación efectiva de dependencias. Esta práctica mejora la fiabilidad y la capacidad de mantenimiento del software, ya que facilita la detección y corrección de problemas en las primeras fases del proceso de desarrollo.

Presentación de IronPDF

Descripción general de la documentación y las funciones de IronPDF es una potente biblioteca de C# que permite a los desarrolladores trabajar con documentos PDF dentro de sus aplicaciones. Ofrece una amplia gama de funciones, como crear, modificar y convertir archivos PDF a partir de diversas fuentes, como HTML, imágenes y PDF existentes. Cuando se combina con el concepto de objetos de imitación como se discutió en el tutorial anterior, IronPDF puede ser una herramienta valiosa para generar y manipular documentos PDF en sus pruebas unitarias.

La principal característica de IronPDF es suConversión de HTML a PDF para garantizar que los diseños y estilos permanezcan intactos. Convierte el contenido web en PDF, lo que lo hace perfecto para informes, facturas y documentación. Esta función permite convertir archivos HTML, URL y cadenas HTML en PDF.

using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
Imports IronPdf

Friend Class Program
	Shared Sub Main(ByVal args() As String)
		Dim renderer = New ChromePdfRenderer()

		' 1. Convert HTML String to PDF
		Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
		Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
		pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")

		' 2. Convert HTML File to PDF
		Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
		Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
		pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")

		' 3. Convert URL to PDF
		Dim url = "http://ironpdf.com" ' Specify the URL
		Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
		pdfFromUrl.SaveAs("URLToPDF.pdf")
	End Sub
End Class
VB   C#

Por ejemplo, si tiene un proyecto que implique la generación o el procesamiento de PDF, puede utilizar IronPDF para crear documentos PDF simulados que imiten situaciones reales. Esto puede ser especialmente útil para probar y validar cómo interactúa su código con los archivos PDF. Puede generar PDF simulados con contenido, diseños y propiedades específicos, y utilizarlos como accesorios de prueba para asegurarse de que su código produce los resultados PDF deseados o gestiona correctamente las operaciones relacionadas con PDF.

Crear objetos simulados para generar archivos PDF

Supongamos que está desarrollando una aplicación que genera informes financieros, y que estos informes deben guardarse y distribuirse como documentos PDF. En este caso, es posible que desee probar la generación del PDF y asegurarse de que el contenido y el formato son correctos.

En primer lugar, tenemos que añadir IronPDF a nuestro proyecto. Escriba el siguiente comando en la consola del gestor de paquetes NuGet para instalar IronPDF.

Install-Package IronPdf

Este comando instalará y añadirá las dependencias necesarias a nuestro proyecto.

A continuación se explica cómo incorporar IronPDF al proceso de pruebas unitarias:

Generación de PDF de prueba

Puede utilizar IronPDF para crear documentos PDF simulados con contenido y estilo específicos para imitar informes financieros reales. Estos PDF simulados pueden servir como accesorios de prueba para sus pruebas unitarias, como se muestra en el siguiente fragmento de código:

public class PDFGenerator
{
    public void GenerateFinancialReport(string reportData)
    {
        var renderer = new ChromePdfRenderer();
        // Generate the report HTML
        string reportHtml = GenerateReportHtml(reportData);
        PdfDocument pdfDocument = renderer.RenderHtmlAsPdf(reportHtml);
        // Save the PDF to a file or memory stream
        pdfDocument.SaveAsPdfA("FinancialReport.pdf");
    }

    private string GenerateReportHtml(string reportData)
    {
        // Generate the report HTML based on the provided data
        // (e.g., using Razor views or any HTML templating mechanism)
        // Return the HTML as a string

        return "<h1>my Report</h1>";
    }
}
public class PDFGenerator
{
    public void GenerateFinancialReport(string reportData)
    {
        var renderer = new ChromePdfRenderer();
        // Generate the report HTML
        string reportHtml = GenerateReportHtml(reportData);
        PdfDocument pdfDocument = renderer.RenderHtmlAsPdf(reportHtml);
        // Save the PDF to a file or memory stream
        pdfDocument.SaveAsPdfA("FinancialReport.pdf");
    }

    private string GenerateReportHtml(string reportData)
    {
        // Generate the report HTML based on the provided data
        // (e.g., using Razor views or any HTML templating mechanism)
        // Return the HTML as a string

        return "<h1>my Report</h1>";
    }
}
Public Class PDFGenerator
	Public Sub GenerateFinancialReport(ByVal reportData As String)
		Dim renderer = New ChromePdfRenderer()
		' Generate the report HTML
		Dim reportHtml As String = GenerateReportHtml(reportData)
		Dim pdfDocument As PdfDocument = renderer.RenderHtmlAsPdf(reportHtml)
		' Save the PDF to a file or memory stream
		pdfDocument.SaveAsPdfA("FinancialReport.pdf")
	End Sub

	Private Function GenerateReportHtml(ByVal reportData As String) As String
		' Generate the report HTML based on the provided data
		' (e.g., using Razor views or any HTML templating mechanism)
		' Return the HTML as a string

		Return "<h1>my Report</h1>"
	End Function
End Class
VB   C#

Pruebas unitarias con PDF simulados

Escribiremos pruebas para utilizar IronPDF para generar PDFs simulados que representen varios escenarios de informes. A continuación, compararemos los PDF reales generados por nuestro código con estos PDF simulados para asegurarnos de que el contenido, el formato y la estructura son los esperados.

internal class PDFGeneratorTests
{
    [Test]
    public void GenerateFinancialReport_CreatesCorrectPDF()
    {
        // Arrange
        var mock = new PDFGenerator();
        var expectedPdf = PdfDocument.FromFile("ExpectedFinancialReport.pdf"); // Load a mock PDF

        // Act
        mock.GenerateFinancialReport("Sample report data");
        var actualPdf = PdfDocument.FromFile("FinancialReport.pdf");

        // Assert
        Assert.AreEqual(actualPdf.ExtractAllText() , expectedPdf.ExtractAllText());
    }

}
internal class PDFGeneratorTests
{
    [Test]
    public void GenerateFinancialReport_CreatesCorrectPDF()
    {
        // Arrange
        var mock = new PDFGenerator();
        var expectedPdf = PdfDocument.FromFile("ExpectedFinancialReport.pdf"); // Load a mock PDF

        // Act
        mock.GenerateFinancialReport("Sample report data");
        var actualPdf = PdfDocument.FromFile("FinancialReport.pdf");

        // Assert
        Assert.AreEqual(actualPdf.ExtractAllText() , expectedPdf.ExtractAllText());
    }

}
Friend Class PDFGeneratorTests
	<Test>
	Public Sub GenerateFinancialReport_CreatesCorrectPDF()
		' Arrange
		Dim mock = New PDFGenerator()
		Dim expectedPdf = PdfDocument.FromFile("ExpectedFinancialReport.pdf") ' Load a mock PDF

		' Act
		mock.GenerateFinancialReport("Sample report data")
		Dim actualPdf = PdfDocument.FromFile("FinancialReport.pdf")

		' Assert
		Assert.AreEqual(actualPdf.ExtractAllText(), expectedPdf.ExtractAllText())
	End Sub

End Class
VB   C#

En este código de prueba, generamos un PDF simulado(pdf esperado) que representa el resultado esperado y compararlo con el PDF(PDF real) generado por el PDFGenerator. Hemos extraído el contenido de ambos PDF para verificar si tienen el mismo contenido.

Conclusión

En conclusión, aprovechar el MOQ, junto conFunciones de IronPDF en nuestro proceso de pruebas unitarias, nos permite verificar exhaustivamente el comportamiento de nuestras aplicaciones de software. MOQ nos permite aislar componentes de código específicos, controlar dependencias y simular escenarios complejos, permitiéndonos escribir pruebas centradas y fiables.

Mientras tanto,Características de IronPDF mejorar nuestras capacidades de prueba facilitando la generación y manipulación de documentos PDF, garantizando que nuestras funcionalidades relacionadas con PDF se examinan a fondo. Al integrar estas herramientas en nuestro conjunto de pruebas, podemos desarrollar con confianza un software robusto y de alta calidad que satisfaga las exigencias tanto de funcionalidad como de rendimiento. Esta combinación de pruebas unitarias sólidas con MOQ y validación de PDF con IronPDF contribuye significativamente a la calidad y fiabilidad general de nuestras aplicaciones.

Vale la pena señalar que IronPDF ofrece unPrueba gratuita de IronPDF para probar sus funciones. Si considera que se adapta a sus necesidades, tiene la opción de adquirir unLicencia comercial para IronPDF lo que le permite seguir utilizando las capacidades de IronPDF en sus proyectos con todas las ventajas y el soporte que ofrece una versión con licencia, garantizando la integración sin problemas de las funcionalidades relacionadas con PDF en sus aplicaciones.

< ANTERIOR
Entity Framework C# (Cómo funciona para desarrolladores)
SIGUIENTE >
C# Web Framework (Cómo funciona para desarrolladores)

¿Listo para empezar? Versión: 2024.12 acaba de salir

Descarga gratuita de NuGet Descargas totales: 11,622,374 Ver licencias >