database - una - conectar access con visual basic 2010 mediante codigo
¿La mejor forma de probar una aplicación de MS Access? (12)
1. Escribe el código testable
Primero, deje de escribir lógica comercial en el código de su formulario. Ese no es el lugar para eso. No puede ser probado apropiadamente allí. De hecho, realmente no debería tener que probar su formulario en absoluto. Debería ser una vista muda, muda y simple, que responda a la interacción del usuario y luego delegue la responsabilidad de responder a esas acciones en otra clase que sea comprobable.
¿Cómo haces eso? Familiarizarse con el patrón Model-View-Controller es un buen comienzo.
No se puede hacer perfectamente en VBA debido a que obtenemos eventos o interfaces, nunca ambos, pero puedes acercarte mucho. Considere esta forma simple que tiene un cuadro de texto y un botón.
En el código del formulario que se encuentra detrás, ajustaremos el valor del TextBox en una propiedad pública y volveremos a generar los eventos que nos interesen.
Public Event OnSayHello()
Public Event AfterTextUpdate()
Public Property Let Text(value As String)
Me.TextBox1.value = value
End Property
Public Property Get Text() As String
Text = Me.TextBox1.value
End Property
Private Sub SayHello_Click()
RaiseEvent OnSayHello
End Sub
Private Sub TextBox1_AfterUpdate()
RaiseEvent AfterTextUpdate
End Sub
Ahora necesitamos un modelo para trabajar. Aquí he creado un nuevo módulo de clase llamado MyModel
. Aquí está el código que pondremos a prueba. Tenga en cuenta que, naturalmente, comparte una estructura similar a nuestra vista.
Private mText As String
Public Property Let Text(value As String)
mText = value
End Property
Public Property Get Text() As String
Text = mText
End Property
Public Function Reversed() As String
Dim result As String
Dim length As Long
length = Len(mText)
Dim i As Long
For i = 0 To length - 1
result = result + Mid(mText, (length - i), 1)
Next i
Reversed = result
End Function
Public Sub SayHello()
MsgBox Reversed()
End Sub
Finalmente, nuestro controlador lo conecta todo. El controlador escucha eventos de formulario y comunica los cambios al modelo y activa las rutinas del modelo.
Private WithEvents view As Form_Form1
Private model As MyModel
Public Sub Run()
Set model = New MyModel
Set view = New Form_Form1
view.Visible = True
End Sub
Private Sub view_AfterTextUpdate()
model.Text = view.Text
End Sub
Private Sub view_OnSayHello()
model.SayHello
view.Text = model.Reversed()
End Sub
Ahora este código se puede ejecutar desde cualquier otro módulo. Para los propósitos de este ejemplo, he usado un módulo estándar. Le recomiendo que construya esto usted mismo utilizando el código que he proporcionado y que funcione.
Private controller As FormController
Public Sub Run()
Set controller = New FormController
controller.Run
End Sub
Entonces, eso es genial y todo, ¿pero qué tiene que ver con las pruebas? Amigo, tiene todo que ver con las pruebas. Lo que hemos hecho es hacer que nuestro código sea comprobable . En el ejemplo que proporcioné, no hay ninguna razón para intentar siquiera probar la GUI. Lo único que realmente necesitamos probar es el model
. Ahí es donde está toda la lógica real.
Entonces, en el paso dos.
2. Elija un marco de prueba de la unidad
No hay muchas opciones aquí. La mayoría de los marcos requieren la instalación de complementos COM, mucha placa de caldera, sintaxis extraña, pruebas de escritura como comentarios, etc. Es por eso que me involucré en la creación de uno yo mismo , así que esta parte de mi respuesta no es imparcial, pero lo intentaré para dar un resumen justo de lo que está disponible.
- Funciona solo en Access.
- Requiere que escriba pruebas como un extraño híbrido de comentarios y código. (no intellisense para la parte de comentario.
- Sin embargo, hay una interfaz gráfica para ayudarte a escribir esas pruebas de aspecto extraño.
- El proyecto no ha visto ninguna actualización desde 2013.
Unidad VB Lite No puedo decir que la haya usado personalmente. Está disponible, pero no ha visto una actualización desde 2005.
xlUnit xlUnit no es horrible, pero tampoco es bueno. Es torpe y hay un montón de código de placa de caldera. Es lo mejor de lo peor, pero no funciona en Access. Entonces, eso está fuera.
Crea tu propio marco
He estado allí y he hecho eso . Probablemente sea más de lo que la mayoría de la gente quiere, pero es completamente posible construir un marco de prueba de unidad en código VBA nativo.
El marco de prueba de la unidad del complemento Rubberduck VBE
Descargo de responsabilidad: soy uno de los co-desarrolladores .Soy parcial, pero este es de lejos mi favorito del grupo.
- Poco o ningún código de placa de caldera.
- Intellisense está disponible.
- El proyecto está activo.
- Más documentación que la mayoría de estos proyectos.
- Funciona en la mayoría de las principales aplicaciones de Office, no solo en Access.
- Desafortunadamente, es un complemento COM, por lo que debe instalarse en su máquina.
3. Comience a escribir pruebas
Por lo tanto, volvamos a nuestro código de la sección 1. El único código que realmente necesitamos para probar es la función MyModel.Reversed()
. Entonces, echemos un vistazo a cómo se vería esa prueba. (El ejemplo dado utiliza Rubberduck, pero es una prueba simple y podría traducirse en el marco de su elección).
''@TestModule
Private Assert As New Rubberduck.AssertClass
''@TestMethod
Public Sub ReversedReversesCorrectly()
Arrange:
Dim model As New MyModel
Const original As String = "Hello"
Const expected As String = "olleH"
Dim actual As String
model.Text = original
Act:
actual = model.Reversed
Assert:
Assert.AreEqual expected, actual
End Sub
Pautas para escribir buenas pruebas
- Solo prueba una cosa a la vez.
- Las buenas pruebas solo fallan cuando se introduce un error en el sistema o los requisitos han cambiado.
- No incluya dependencias externas como bases de datos y sistemas de archivos. Estas dependencias externas pueden hacer que las pruebas fallen por razones fuera de su control. En segundo lugar, disminuyen tus pruebas. Si tus pruebas son lentas, no las ejecutarás.
- Use nombres de prueba que describan lo que la prueba está probando. No te preocupes si se alarga. Es más importante que sea descriptivo.
Sé que la respuesta fue un poco larga y tardía, pero espero que ayude a algunas personas a comenzar a escribir pruebas unitarias para su código VBA.
Con el código, los formularios y los datos dentro de la misma base de datos, me pregunto cuáles son las mejores prácticas para diseñar un conjunto de pruebas para una aplicación de Microsoft Access (digamos para Access 2007).
Uno de los principales problemas con los formularios de prueba es que solo unos pocos controles tienen un manejador de hwnd
y otros controles solo obtienen uno que tienen enfoque, lo que hace que la automatización sea bastante opaca ya que no se puede obtener una lista de controles en un formulario.
¿Alguna experiencia para compartir?
Aprecié las respuestas de Knox y David. Mi respuesta estará en algún lugar entre ellos: ¡solo haga formularios que no necesitan ser depurados !
Creo que los formularios deben usarse exclusivamente como lo que son básicamente, es decir, solo como interfaz gráfica, lo que significa que no es necesario depurarlos. El trabajo de depuración se limita a sus módulos y objetos VBA, que es mucho más fácil de manejar.
Por supuesto, existe una tendencia natural a agregar código VBA a formularios y / o controles, especialmente cuando Access le ofrece estos excelentes eventos "después de la Actualización" y "en cambio", pero definitivamente le aconsejo que no ponga ningún formulario o controle código específico. en el módulo del formulario. Esto hace que el mantenimiento y la actualización sean muy costosos, donde el código se divide entre los módulos de VBA y los módulos de formularios / controles.
¡Esto no significa que no puedas usar más este evento AfterUpdate
! Simplemente ponga el código estándar en el evento, así:
Private Sub myControl_AfterUpdate()
CTLAfterUpdate myControl
On Error Resume Next
Eval ("CTLAfterUpdate_MyForm()")
On Error GoTo 0
End sub
Dónde:
CTLAfterUpdate
es un procedimiento estándar que se ejecuta cada vez que se actualiza un control en un formularioCTLAfterUpdateMyForm
es un procedimiento específico que se ejecuta cada vez que se actualiza un control en MyForm
Tengo entonces 2 módulos. El primero es
-
utilityFormEvents
donde tendré mi evento genérico CTLAfterUpdate
El segundo es
-
MyAppFormEvents
que contiene el código específico de todas las formas específicas de la aplicación MyApp e incluye el procedimiento CTLAfterUpdateMyForm. Por supuesto, CTLAfterUpdateMyForm podría no existir si no hay un código específico para ejecutar. Es por eso que activamos el "En error" para "reanudar el próximo" ...
Elegir una solución tan genérica significa mucho. Significa que está alcanzando un alto nivel de normalización de código (es decir, mantenimiento sin problemas del código). Y cuando dice que no tiene ningún código específico, también significa que los módulos de formulario están completamente estandarizados y su producción puede automatizarse : simplemente diga qué eventos desea gestionar en el nivel de formulario / control, y defina su terminología de procedimientos genéricos / específicos.
Escriba su código de automatización, una vez por todas.
Toma unos días de trabajo pero da resultados emocionantes. He estado usando esta solución durante los últimos 2 años y es claramente la correcta: mis formularios se crean completa y automáticamente desde cero con una "Tabla de formularios", vinculada a una "Tabla de controles".
Entonces puedo dedicar mi tiempo a trabajar en los procedimientos específicos del formulario, si corresponde.
La normalización de código, incluso con MS Access, es un proceso largo. ¡Pero realmente vale la pena!
Aquí hay buenas sugerencias, pero me sorprende que nadie mencionó el procesamiento centralizado de errores. Puede obtener complementos que permiten funciones rápidas / plantillas secundarias y para agregar números de línea (utilizo herramientas MZ). A continuación, envíe todos los errores a una única función donde pueda registrarlos. También puede romper todos los errores configurando un único punto de interrupción.
Aunque esa es una respuesta muy antigua:
Existe AccUnit , un marco especializado de pruebas de unidades para Microsoft Access.
Diseñaría la aplicación para tener tanto trabajo como sea posible en consultas y en subrutinas vba, de modo que sus pruebas podrían estar compuestas de bases de datos de prueba, ejecutando conjuntos de consultas de producción y vba contra esas bases de datos y luego mirando la salida y comparando para asegurarse de que la salida sea buena. Este enfoque no prueba la GUI obviamente, por lo que podría aumentar las pruebas con una serie de scripts de prueba (aquí me refiero a un documento de Word que dice abrir formulario 1, y hacer clic en control 1) que se ejecutan manualmente.
Depende del alcance del proyecto como el nivel de automatización necesario para el aspecto de prueba.
El acceso es una aplicación COM. Use COM, no API de Windows. para probar cosas en Access.
El mejor entorno de prueba para una aplicación de acceso es acceso. Todos sus Formularios / Informes / Tablas / Código / Consultas están disponibles, existe un lenguaje de scripts similar al de MS Test (vale, probablemente no recuerde MS Test), existe un entorno de base de datos para guardar los scripts de prueba y los resultados de las pruebas, y las habilidades que construyes aquí son transferibles a tu aplicación.
Encuentro que hay relativamente pocas oportunidades para probar unidades en mis aplicaciones. La mayor parte del código que escribo interactúa con los datos de la tabla o el sistema de archivo, por lo que es fundamentalmente difícil de probar. Al principio, probé un enfoque que puede ser similar a la burla (suplantación) donde creé un código que tenía un parámetro opcional. Si se usó el parámetro, entonces el procedimiento usaría el parámetro en lugar de buscar datos de la base de datos. Es bastante fácil configurar un tipo definido por el usuario que tenga los mismos tipos de campo que una fila de datos y pasarlo a una función. Ahora tengo una manera de obtener datos de prueba en el procedimiento que quiero probar. Dentro de cada procedimiento, había algún código que intercambiaba la fuente de datos real para la fuente de datos de prueba. Esto me permitió usar pruebas unitarias en una variedad más amplia de funciones, usando mis propias funciones de prueba de unidades. La prueba de unidad de escritura es fácil, solo es repetitiva y aburrida. Al final, abandoné las pruebas unitarias y empecé a usar un enfoque diferente.
Escribo aplicaciones internas para mí principalmente, así que puedo permitirme esperar hasta que los problemas me encuentren en lugar de tener que tener un código perfecto. Si escribo aplicaciones para clientes, generalmente el cliente no está al tanto de cuánto cuesta el desarrollo de software, así que necesito una forma de obtener resultados a bajo costo. Escribir pruebas unitarias tiene que ver con escribir una prueba que envía datos incorrectos en un procedimiento para ver si el procedimiento puede manejarlo de manera adecuada. Las pruebas unitarias también confirman que los buenos datos se manejan de manera adecuada. Mi enfoque actual se basa en escribir validación de entrada en cada procedimiento dentro de una aplicación y generar una bandera de éxito cuando el código se ha completado con éxito. Cada procedimiento de llamada busca el indicador de éxito antes de usar el resultado. Si ocurre un problema, se informa por medio de un mensaje de error. Cada función tiene un indicador de éxito, un valor de retorno, un mensaje de error, un comentario y un origen. Un tipo definido por el usuario (fr para el retorno de la función) contiene los miembros de datos. Cualquier función dada llena muchos de los miembros de datos en el tipo definido por el usuario. Cuando se ejecuta una función, por lo general devuelve success = true y un valor de retorno y, a veces un comentario. Si una función falla, devuelve success = false y un mensaje de error. Si una cadena de funciones falla, los mensajes de error se cambian en margarita, pero el resultado es en realidad mucho más legible que un seguimiento de pila normal. Los orígenes también están encadenados, así que sé dónde ocurrió el problema. La aplicación rara vez se cuelga e informa con precisión cualquier problema. El resultado es muchísimo mejor que el manejo de errores estándar.
Public Function GetOutputFolder(OutputFolder As eOutputFolder) As FunctRet
''///Returns a full path when provided with a target folder alias. e.g. ''temp'' folder
Dim fr As FunctRet
Select Case OutputFolder
Case 1
fr.Rtn = "C:/Temp/"
fr.Success = True
Case 2
fr.Rtn = TrailingSlash(Application.CurrentProject.path)
fr.Success = True
Case 3
fr.EM = "Can''t set custom paths – not yet implemented"
Case Else
fr.EM = "Unrecognised output destination requested"
End Select
exitproc:
GetOutputFolder = fr
End Function
Código explicado eOutputFolder es un Enum definido por el usuario como se muestra a continuación
Public Enum eOutputFolder
eDefaultDirectory = 1
eAppPath = 2
eCustomPath = 3
End Enum
Estoy usando Enum para pasar parámetros a las funciones, ya que esto crea un conjunto limitado de opciones conocidas que una función puede aceptar. Los enumeraciones también proporcionan intellisense al ingresar parámetros en las funciones. Supongo que proporcionan una interfaz rudimentaria para una función.
''Type FunctRet is used as a generic means of reporting function returns
Public Type FunctRet
Success As Long ''Boolean flag for success, boolean not used to avoid nulls
Rtn As Variant ''Return Value
EM As String ''Error message
Cmt As String ''Comments
Origin As String ''Originating procedure/function
End Type
Un tipo definido por el usuario como FunctRet también proporciona la finalización de código que ayuda. Dentro del procedimiento, generalmente almacé los resultados internos en una variable interna anónima (fr) antes de asignar los resultados a la variable de devolución (GetOutputFolder). Esto hace que los procedimientos de cambio de nombre sean muy fáciles ya que solo se deben cambiar la parte superior e inferior.
Entonces, en resumen, he desarrollado un framework con ms-access que cubre todas las operaciones que involucran a VBA. La prueba está escrita permanentemente en los procedimientos, en lugar de una prueba de unidad de tiempo de desarrollo. En la práctica, el código todavía corre muy rápido. Tengo mucho cuidado para optimizar las funciones de nivel inferior que se pueden llamar diez mil veces por minuto. Además, puedo usar el código en producción mientras se desarrolla. Si se produce un error, es fácil de usar y la fuente y el motivo del error suelen ser obvios. Los errores se informan desde el formulario de llamada, no desde algún módulo en la capa empresarial, que es un principio importante del diseño de la aplicación. Además, no tengo la carga de mantener el código de pruebas unitarias, que es realmente importante cuando estoy desarrollando un diseño en lugar de codificar un diseño claramente conceptualizado.
Hay algunos problemas potenciales. Las pruebas no son automáticas y el nuevo código incorrecto solo se detecta cuando se ejecuta la aplicación. El código no se parece al código VBA estándar (generalmente es más corto). Aún así, el enfoque tiene algunas ventajas. Es mucho mejor que usar un controlador de errores solo para registrar un error ya que los usuarios generalmente me contactarán y darán un mensaje de error significativo. También puede manejar procedimientos que funcionan con datos externos. JavaScript me recuerda a VBA, me pregunto por qué JavaScript es la tierra de los frameworks y VBA en ms-access no lo es.
Unos días después de escribir esta publicación, encontré un artículo sobre The CodeProject que se acerca mucho a lo que escribí arriba. El artículo compara y contrasta el manejo de excepciones y el manejo de errores. Lo que he sugerido anteriormente es similar al manejo de excepciones.
Las páginas de acceso a datos han sido desaprobadas por MS por bastante tiempo, y nunca funcionaron en primer lugar (dependían de que se instalaran los widgets de Office, y funcionaran solo en IE, y solo de mala manera).
Es cierto que los controles de Acceso que pueden obtener el foco solo tienen un identificador de ventana cuando tienen el foco (y aquellos que no pueden enfocarse, como las etiquetas, nunca tienen un identificador de ventana en absoluto). Esto hace que Access sea singularmente inadecuado para los regímenes de prueba manejados por la ventana.
De hecho, me pregunto por qué quieres hacer este tipo de pruebas en Access. Me parece que es un dogma básico de Extreme Programming, y no todos los principios y prácticas de XP se pueden adaptar para trabajar con aplicaciones Access: Square Peg, Round Hole.
Por lo tanto, dé un paso atrás y pregúntese qué está tratando de lograr y considere que es posible que necesite utilizar métodos completamente diferentes a los que se basan en los enfoques que simplemente no pueden funcionar en Access.
O si ese tipo de prueba automatizada es válida o incluso útil con una aplicación de Access.
No he intentado esto, pero podría intentar publicar sus formularios de acceso como páginas web de acceso a datos en algo parecido a compartir o simplemente como páginas web y luego usar una herramienta como el selenium para conducir el navegador con un conjunto de pruebas.
Obviamente, esto no es tan ideal como conducir el código directamente a través de pruebas unitarias, pero puede que te lleve a una parte del camino. buena suerte
Otra ventaja de que Access es una aplicación COM es que puede crear una aplicación .NET para ejecutar y probar una aplicación de Access a través de la automatización . La ventaja de esto es que luego puede usar un marco de prueba más poderoso como NUnit para escribir pruebas automáticas de afirmación contra una aplicación de Access.
Por lo tanto, si dominas C # o VB.NET combinados con algo así como NUnit, entonces puedes crear más fácilmente una mayor cobertura de prueba para tu aplicación de Access.
Saqué una página del concepto más preciso de Python e implementé un procedimiento de DocTests en Access VBA. Obviamente, esta no es una solución de pruebas de unidades en toda regla. Todavía es relativamente joven, así que dudo que haya resuelto todos los errores, pero creo que es lo suficientemente maduro como para salir al aire libre.
Simplemente copie el siguiente código en un módulo de código estándar y presione F5 dentro del Sub para verlo en acción:
''>>> 1 + 1
''2
''>>> 3 - 1
''0
Sub DocTests()
Dim Comp As Object, i As Long, CM As Object
Dim Expr As String, ExpectedResult As Variant, TestsPassed As Long, TestsFailed As Long
Dim Evaluation As Variant
For Each Comp In Application.VBE.ActiveVBProject.VBComponents
Set CM = Comp.CodeModule
For i = 1 To CM.CountOfLines
If Left(Trim(CM.Lines(i, 1)), 4) = "''>>>" Then
Expr = Trim(Mid(CM.Lines(i, 1), 5))
On Error Resume Next
Evaluation = Eval(Expr)
If Err.Number = 2425 And Comp.Type <> 1 Then
''The expression you entered has a function name that '''' can''t find.
''This is not surprising because we are not in a standard code module (Comp.Type <> 1).
''So we will just ignore it.
GoTo NextLine
ElseIf Err.Number <> 0 Then
Debug.Print Err.Number, Err.Description, Expr
GoTo NextLine
End If
On Error GoTo 0
ExpectedResult = Trim(Mid(CM.Lines(i + 1, 1), InStr(CM.Lines(i + 1, 1), "''") + 1))
Select Case ExpectedResult
Case "True": ExpectedResult = True
Case "False": ExpectedResult = False
Case "Null": ExpectedResult = Null
End Select
Select Case TypeName(Evaluation)
Case "Long", "Integer", "Short", "Byte", "Single", "Double", "Decimal", "Currency"
ExpectedResult = Eval(ExpectedResult)
Case "Date"
If IsDate(ExpectedResult) Then ExpectedResult = CDate(ExpectedResult)
End Select
If (Evaluation = ExpectedResult) Then
TestsPassed = TestsPassed + 1
ElseIf (IsNull(Evaluation) And IsNull(ExpectedResult)) Then
TestsPassed = TestsPassed + 1
Else
Debug.Print Comp.Name; ": "; Expr; " evaluates to: "; Evaluation; " Expected: "; ExpectedResult
TestsFailed = TestsFailed + 1
End If
End If
NextLine:
Next i
Next Comp
Debug.Print "Tests passed: "; TestsPassed; " of "; TestsPassed + TestsFailed
End Sub
Copiar, pegar y ejecutar el código anterior desde un módulo llamado Module1 produce:
Module: 3 - 1 evaluates to: 2 Expected: 0
Tests passed: 1 of 2
Algunas notas rápidas:
- No tiene dependencias (cuando se usa desde Access)
- Hace uso de
Eval
que es una función en el modelo de objetos Access.Application; esto significa que podría usarlo fuera de Access pero requeriría la creación de un objeto Access.Application y la calificación completa de las llamadasEval
- Hay algunas idiosincrasias asociadas con
Eval
para tener en cuenta - Solo se puede usar en funciones que devuelven un resultado que se ajusta a una sola línea
A pesar de sus limitaciones, sigo creyendo que ofrece un buen rendimiento por tu dinero.
Editar : Aquí hay una función simple con "reglas de doctest" que la función debe cumplir.
Public Function AddTwoValues(ByVal p1 As Variant, _
ByVal p2 As Variant) As Variant
''>>> AddTwoValues(1,1)
''2
''>>> AddTwoValues(1,1) = 1
''False
''>>> AddTwoValues(1,Null)
''Null
''>>> IsError(AddTwoValues(1,"foo"))
''True
On Error GoTo ErrorHandler
AddTwoValues = p1 + p2
ExitHere:
On Error GoTo 0
Exit Function
ErrorHandler:
AddTwoValues = CVErr(Err.Number)
GoTo ExitHere
End Function
Si está interesado en probar su aplicación de Access a un nivel más detallado, específicamente el código VBA, la unidad VB Lite es un gran marco de prueba de unidades para tal fin.