Skip to content
Prev 366971 / 398506 Next

Using a mock of an S4 class

On 02/01/2017 02:46 PM, Ramiro Barrantes wrote:
I don't know of a convenient way to create a mock with functionality 
like mocks in other languages. But here's a class

   .A = setClass("A", contains="integer")

This creates an instance that might be used as a mock

    mock = .A()  # same as new("A")

but maybe you have an initialize method (initialize methods are very 
tricky to get correct, and many people avoid them, using 
plain-old-functions to form an API around object creation; the 
plain-old-function finishes by calling the constructor .A() or new("A")) 
that has side effects that are inappropriate for your test, mimicked 
here with stop()

   setMethod("initialize", "A", function(.Object, ...) stop("oops"))

our initial attempts are thwarted

 > .A()
Error in initialize(value, ...) : oops

but we could reach into our bag of hacks and try

   mock = .Call(methods:::C_new_object, getClassDef("A"))

You would still need to populate slots / data used in your test, e.g.,

   slot(mock, ".Data") = 1:4

This is robust to any validity method, since the validity method is not 
invoked on direct slot assignment

   setValidity("A", function(object) {
       if (all(object > 0)) TRUE else "oops2"
   })

   slot(mock, ".Data") = 0:4  # still works

So something like

   mockS4object = function(class, ..., where=topenv(parent.frame())) {
       obj <- .Call(
           methods:::C_new_object,
           getClassDef(class, where=where)
       )

       args = list(...)
       for (nm in names(args))
           slot(obj, nm) = args[[nm]]

       obj
   }
   mockS4object("A", .Data=1:4)

Mock objects typically have useful testing properties, like returning 
the number of times a slot (field) is accessed. Unfortunately, I don't 
have anything to offer for that.

Martin
This email message may contain legally privileged and/or...{{dropped:2}}