como falsificar programaticamente um evento de toque em um UIButton?

183

Estou escrevendo alguns testes de unidade e, devido à natureza desse aplicativo em particular, é importante que eu suba no nível UI possível cadeia. Então, o que eu gostaria de fazer é acionar programaticamente um pressionamento de botão, como se o usuário tivesse pressionado o botão no GUI.

(Sim, sim - eu poderia simplesmente chamar oIBAction seletor, mas, novamente, a natureza desse aplicativo em particular torna importante que eu falsifique o pressionamento real do botão, de modo que IBActionseja chamado pelo próprio botão.)

Qual é o método preferido para fazer isso?

Olie
fonte
Você poderia explicar por que é importante falsificar o toque do botão em vez de chamar o método de ação diretamente? Realmente não há diferença prática.
Jasarien 27/10/10
1
@ Jasarien: bem, porque há uma diferença! ;) No meu aplicativo, vários botões usam o mesmo método de ação, e o método analisa como se comportar com base na tag do botão. Portanto, o teste de unidade, além de testar um caminho de código específico através do método, (a) precisa testá-los todos e (b) também verifica se os botões estão conectados corretamente. Além disso, ele abstrai bem o meu conjunto de testes de unidade, pois eu posso definir testes de unidade como linhas em um arquivo de texto com algo como isto: "1 + 2 = 3" onde "3" é a exibição final esperada e tudo o que está antes é qual botão apertar.
Olie
bem, seu método de ação não possui um parâmetro de emissor (id)? Você não poderia simplesmente chamar o método e passar o botão como remetente?
Jasarien 27/10/10
3
Devido à natureza abstrata do meu equipamento de teste, não sei o método que precisa ser chamado para um botão específico. Suponho que eu poderia cavar o botão e obtê-lo, mas enviar sendActionsForControlEvents: faz tudo isso para mim, com menos oportunidade de erro, e mantém o teste abstrato e adequado ao design.
Olie

Respostas:

459

Acontece que

[buttonObj sendActionsForControlEvents: UIControlEventTouchUpInside];

me deu exatamente o que eu precisava, neste caso.

EDIT: Não se esqueça de fazer isso no thread principal, para obter resultados semelhantes aos da imprensa do usuário.


Para o Swift 3:

buttonObj.sendActions(for: .touchUpInside)

Olie
fonte
O que não é diferente de chamar[myController myActionMethod:myButton1];
Jasarien 27/10/10
11
você tem muita restrição nas respostas aos seus comentários;) #
455 Dan Rosenstark
O @Jasarien é bem diferente, pois enviará o evento de toque a todos os alvos registrados (ouvintes).
Htafoya
2
Infelizmente, nenhum deles funcionará sempre - eles confiam no receptor como um UIControl, em vez de, por exemplo, ter um gesto de toque.
tooluser
1
@ChenLiYong, Uma razão seria se myActionMethod:não for pública e, portanto, você não pode chamá-lo diretamente.
Iulian Onofrei 22/03
50

Uma atualização para esta resposta para Swift

buttonObj.sendActionsForControlEvents(.TouchUpInside)

EDIT : Atualizado para Swift 3

buttonObj.sendActions(for: .touchUpInside)
Hellojeffy
fonte
1
Eu diria que: buttonObj.sendActionsForControlEvents (.TouchUpInside) é ainda mais estilo Swift;)
Sajjon
1
Eu não acho que isso funcionará se o botão for criado em um XCTestCase sem um loop de execução compatível.
CopperCash
6

Se você deseja fazer esse tipo de teste, adorará o suporte à UI Automation no iOS 4. Você pode escrever JavaScript para simular pressionamentos de botão, etc. com bastante facilidade, embora a documentação (especialmente a parte de introdução) seja um pouco escasso.

Jeff Kelley
fonte
1
Sim, isso é legal coisas, mas eu estou tentando fazê-lo de dentro do aplicativo, não via Instruments (se esse é o método que você quer dizer.)
Olie
O Instruments é executado com seu aplicativo. O aplicativo ainda está sendo executado, o Instruments simula o toque.
Jeff Kelley
6
Entendo a Instruments, Jeff - o que estou dizendo é: quero que meus testes de unidade sejam executados no dispositivo no meu bolso, sem um laptop, XCode ou Instruments à mão. Ou você conhece o segredo de executar instrumentos no dispositivo? ;)
Olie
6

Swift 3:

self.btn.sendActions(for: .touchUpInside)
Gobi M
fonte
3
Isso está apenas repetindo as respostas mais pontuadas.
Pang
5

Nesse caso, UIButtoné derivado de UIControl. Isso funciona para o objeto derivado de UIControl.

Queria reutilizar " UIBarButtonItem" ações em casos de uso específicos. Aqui, UIBarButtonItemnão oferece métodosendActionsForControlEvents:

Mas, felizmente, UIBarButtonItempossui propriedades para destino e ação.

 if(notHappy){        
         SEL exit = self.navigationItem.rightBarButtonItem.action;
         id  world = self.navigationItem.rightBarButtonItem.target;
         [world performSelector:exit];
 }

Aqui, rightBarButtonItemé do tipo UIBarButtonItem.

Hitesh Savaliya
fonte
se você usar UIBarButtonItem, poderá apenas [self doAction: self.navigationItem.rightBarButtonItem]; // - doAction: é o nome da ação de alternância de self.navigationItem.rightBarButtonItem.
c0ming
5

Para Xamarin iOS

btnObj.SendActionForControlEvents(UIControlEvent.TouchUpInside);

Referência

Durai Amuthan.H
fonte
-3

Swift 4:

self .yourButton (self)

ELEMENTO Arts
fonte